snarkvm_circuit_algorithms/pedersen/
commit_uncompressed.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 NUM_BITS: u8> CommitUncompressed for Pedersen<E, NUM_BITS> {
19    type Input = Boolean<E>;
20    type Output = Group<E>;
21    type Randomizer = Scalar<E>;
22
23    /// Returns the Pedersen commitment of the given input and randomizer as an affine group element.
24    fn commit_uncompressed(&self, input: &[Self::Input], randomizer: &Self::Randomizer) -> Self::Output {
25        let hash = self.hash_uncompressed(input);
26
27        // Compute h^r
28        randomizer
29            .to_bits_le()
30            .iter()
31            .zip_eq(&self.random_base)
32            .map(|(bit, power)| Group::ternary(bit, power, &Group::zero()))
33            .fold(hash, |acc, x| acc + x)
34    }
35}
36
37impl<E: Environment, const NUM_BITS: u8>
38    Metrics<dyn CommitUncompressed<Input = Boolean<E>, Output = Group<E>, Randomizer = Scalar<E>>>
39    for Pedersen<E, NUM_BITS>
40{
41    type Case = (Vec<Mode>, Vec<Mode>);
42
43    fn count(case: &Self::Case) -> Count {
44        let (input_modes, randomizer_modes) = case;
45        let uncompressed_count =
46            count!(Pedersen<E, NUM_BITS>, HashUncompressed<Input = Boolean<E>, Output = Group<E>>, input_modes);
47        let uncompressed_mode =
48            output_mode!(Pedersen<E, NUM_BITS>, HashUncompressed<Input = Boolean<E>, Output = Group<E>>, input_modes);
49
50        // Compute the const of constructing the group elements.
51        let group_initialize_count = randomizer_modes
52            .iter()
53            .map(|mode| {
54                count!(
55                    Group<E>,
56                    Ternary<Boolean = Boolean<E>, Output = Group<E>>,
57                    &(*mode, Mode::Constant, Mode::Constant)
58                )
59            })
60            .fold(Count::zero(), |cumulative, count| cumulative + count);
61
62        // Compute the count for converting the randomizer into bits.
63        let randomizer_to_bits_count =
64            match Mode::combine(randomizer_modes[0], randomizer_modes.iter().copied()).is_constant() {
65                true => Count::is(251, 0, 0, 0),
66                false => Count::is(0, 0, 501, 503),
67            };
68
69        // Determine the modes of each of the group elements.
70        let modes = randomizer_modes.iter().map(|mode| {
71            // The `first` and `second` inputs to `Group::ternary` are always constant so we can directly determine the mode instead of
72            // using the `output_mode` macro. This avoids the need to use `CircuitType` as a parameter, simplifying the logic of this function.
73            match mode.is_constant() {
74                true => Mode::Constant,
75                false => Mode::Private,
76            }
77        });
78
79        // Calculate the cost of summing the group elements.
80        let (_, summation_count) =
81            modes.fold((uncompressed_mode, Count::zero()), |(prev_mode, cumulative), curr_mode| {
82                let mode = output_mode!(Group<E>, Add<Group<E>, Output = Group<E>>, &(prev_mode, curr_mode));
83                let sum_count = count!(Group<E>, Add<Group<E>, Output = Group<E>>, &(prev_mode, curr_mode));
84                (mode, cumulative + sum_count)
85            });
86
87        // Compute the cost of summing the hash and random elements.
88        uncompressed_count + group_initialize_count + randomizer_to_bits_count + summation_count
89    }
90}
91
92impl<E: Environment, const NUM_BITS: u8>
93    OutputMode<dyn CommitUncompressed<Input = Boolean<E>, Output = Group<E>, Randomizer = Scalar<E>>>
94    for Pedersen<E, NUM_BITS>
95{
96    type Case = (Vec<Mode>, Vec<Mode>);
97
98    fn output_mode(parameters: &Self::Case) -> Mode {
99        let (input_modes, randomizer_modes) = parameters;
100        match input_modes.iter().all(|m| *m == Mode::Constant) && randomizer_modes.iter().all(|m| *m == Mode::Constant)
101        {
102            true => Mode::Constant,
103            false => Mode::Private,
104        }
105    }
106}
107
108#[cfg(all(test, feature = "console"))]
109mod tests {
110    use super::*;
111    use snarkvm_circuit_types::environment::Circuit;
112    use snarkvm_utilities::{TestRng, Uniform};
113
114    const ITERATIONS: u64 = 10;
115    const MESSAGE: &str = "PedersenCircuit0";
116    const NUM_BITS_MULTIPLIER: u8 = 8;
117
118    fn check_commit_uncompressed<const NUM_BITS: u8>(mode: Mode, rng: &mut TestRng) {
119        use console::CommitUncompressed as C;
120
121        // Initialize Pedersen.
122        let native = console::Pedersen::<<Circuit as Environment>::Network, NUM_BITS>::setup(MESSAGE);
123        let circuit = Pedersen::<Circuit, NUM_BITS>::constant(native.clone());
124
125        for i in 0..ITERATIONS {
126            // Sample a random input.
127            let input = (0..NUM_BITS).map(|_| bool::rand(rng)).collect::<Vec<bool>>();
128            // Sample a randomizer.
129            let randomizer = Uniform::rand(rng);
130            // Compute the expected commitment.
131            let expected = native.commit_uncompressed(&input, &randomizer).expect("Failed to commit native input");
132            // Prepare the circuit input.
133            let circuit_input: Vec<Boolean<_>> = Inject::new(mode, input);
134            // Prepare the circuit randomizer.
135            let circuit_randomizer: Scalar<_> = Inject::new(mode, randomizer);
136
137            Circuit::scope(format!("Pedersen {mode} {i}"), || {
138                // Perform the commit operation.
139                let candidate = circuit.commit_uncompressed(&circuit_input, &circuit_randomizer);
140                assert_eq!(expected, candidate.eject_value());
141
142                // Check constraint counts and output mode.
143                let input_modes = circuit_input.iter().map(|b| b.eject_mode()).collect::<Vec<_>>();
144                let randomizer_modes =
145                    circuit_randomizer.to_bits_le().iter().map(|b| b.eject_mode()).collect::<Vec<_>>();
146                assert_count!(
147                    Pedersen<Circuit, NUM_BITS>,
148                    CommitUncompressed<Input = Boolean<Circuit>, Output = Group<Circuit>, Randomizer = Scalar<Circuit>>,
149                    &(input_modes.clone(), randomizer_modes.clone())
150                );
151                assert_output_mode!(
152                    Pedersen<Circuit, NUM_BITS>,
153                    CommitUncompressed<Input = Boolean<Circuit>, Output = Group<Circuit>, Randomizer = Scalar<Circuit>>,
154                    &(input_modes, randomizer_modes),
155                    candidate
156                );
157            });
158        }
159    }
160
161    fn check_homomorphic_addition<C: Display + Eject + Add<Output = C> + ToBits<Boolean = Boolean<Circuit>>>(
162        pedersen: &impl CommitUncompressed<Input = Boolean<Circuit>, Randomizer = Scalar<Circuit>, Output = Group<Circuit>>,
163        first: C,
164        second: C,
165        rng: &mut TestRng,
166    ) {
167        println!("Checking homomorphic addition on {first} + {second}");
168
169        // Sample the circuit randomizers.
170        let first_randomizer: Scalar<_> = Inject::new(Mode::Private, Uniform::rand(rng));
171        let second_randomizer: Scalar<_> = Inject::new(Mode::Private, Uniform::rand(rng));
172
173        // Compute the expected commitment, by committing them individually and summing their results.
174        let a = pedersen.commit_uncompressed(&first.to_bits_le(), &first_randomizer);
175        let b = pedersen.commit_uncompressed(&second.to_bits_le(), &second_randomizer);
176        let expected = a + b;
177
178        let combined_randomizer = first_randomizer + second_randomizer;
179
180        // Sum the two integers, and then commit the sum.
181        let candidate = pedersen.commit_uncompressed(&(first + second).to_bits_le(), &combined_randomizer);
182        assert_eq!(expected.eject(), candidate.eject());
183        assert!(Circuit::is_satisfied());
184    }
185
186    #[test]
187    fn test_commit_uncompressed_constant() {
188        // Set the number of windows, and modulate the window size.
189        let mut rng = TestRng::default();
190        check_commit_uncompressed::<NUM_BITS_MULTIPLIER>(Mode::Constant, &mut rng);
191        check_commit_uncompressed::<{ 2 * NUM_BITS_MULTIPLIER }>(Mode::Constant, &mut rng);
192        check_commit_uncompressed::<{ 3 * NUM_BITS_MULTIPLIER }>(Mode::Constant, &mut rng);
193        check_commit_uncompressed::<{ 4 * NUM_BITS_MULTIPLIER }>(Mode::Constant, &mut rng);
194        check_commit_uncompressed::<{ 5 * NUM_BITS_MULTIPLIER }>(Mode::Constant, &mut rng);
195    }
196
197    #[test]
198    fn test_commit_uncompressed_public() {
199        // Set the number of windows, and modulate the window size.
200        let mut rng = TestRng::default();
201        check_commit_uncompressed::<NUM_BITS_MULTIPLIER>(Mode::Public, &mut rng);
202        check_commit_uncompressed::<{ 2 * NUM_BITS_MULTIPLIER }>(Mode::Public, &mut rng);
203        check_commit_uncompressed::<{ 3 * NUM_BITS_MULTIPLIER }>(Mode::Public, &mut rng);
204        check_commit_uncompressed::<{ 4 * NUM_BITS_MULTIPLIER }>(Mode::Public, &mut rng);
205        check_commit_uncompressed::<{ 5 * NUM_BITS_MULTIPLIER }>(Mode::Public, &mut rng);
206    }
207
208    #[test]
209    fn test_commit_uncompressed_private() {
210        // Set the number of windows, and modulate the window size.
211        let mut rng = TestRng::default();
212        check_commit_uncompressed::<NUM_BITS_MULTIPLIER>(Mode::Private, &mut rng);
213        check_commit_uncompressed::<{ 2 * NUM_BITS_MULTIPLIER }>(Mode::Private, &mut rng);
214        check_commit_uncompressed::<{ 3 * NUM_BITS_MULTIPLIER }>(Mode::Private, &mut rng);
215        check_commit_uncompressed::<{ 4 * NUM_BITS_MULTIPLIER }>(Mode::Private, &mut rng);
216        check_commit_uncompressed::<{ 5 * NUM_BITS_MULTIPLIER }>(Mode::Private, &mut rng);
217    }
218
219    #[test]
220    fn test_pedersen64_homomorphism_private() {
221        // Initialize Pedersen64.
222        let pedersen = Pedersen64::constant(console::Pedersen64::setup("Pedersen64HomomorphismTest"));
223
224        let mut rng = TestRng::default();
225
226        for _ in 0..ITERATIONS {
227            // Sample two random unsigned integers, with the MSB set to 0.
228            let first = U8::<Circuit>::new(Mode::Private, console::U8::new(u8::rand(&mut rng) >> 1));
229            let second = U8::new(Mode::Private, console::U8::new(u8::rand(&mut rng) >> 1));
230            check_homomorphic_addition(&pedersen, first, second, &mut rng);
231
232            // Sample two random unsigned integers, with the MSB set to 0.
233            let first = U16::<Circuit>::new(Mode::Private, console::U16::new(u16::rand(&mut rng) >> 1));
234            let second = U16::new(Mode::Private, console::U16::new(u16::rand(&mut rng) >> 1));
235            check_homomorphic_addition(&pedersen, first, second, &mut rng);
236
237            // Sample two random unsigned integers, with the MSB set to 0.
238            let first = U32::<Circuit>::new(Mode::Private, console::U32::new(u32::rand(&mut rng) >> 1));
239            let second = U32::new(Mode::Private, console::U32::new(u32::rand(&mut rng) >> 1));
240            check_homomorphic_addition(&pedersen, first, second, &mut rng);
241
242            // Sample two random unsigned integers, with the MSB set to 0.
243            let first = U64::<Circuit>::new(Mode::Private, console::U64::new(u64::rand(&mut rng) >> 1));
244            let second = U64::new(Mode::Private, console::U64::new(u64::rand(&mut rng) >> 1));
245            check_homomorphic_addition(&pedersen, first, second, &mut rng);
246        }
247    }
248
249    #[test]
250    fn test_pedersen_homomorphism_private() {
251        fn check_pedersen_homomorphism(
252            pedersen: &impl CommitUncompressed<
253                Input = Boolean<Circuit>,
254                Randomizer = Scalar<Circuit>,
255                Output = Group<Circuit>,
256            >,
257        ) {
258            let mut rng = TestRng::default();
259
260            for _ in 0..ITERATIONS {
261                // Sample two random unsigned integers, with the MSB set to 0.
262                let first = U8::<Circuit>::new(Mode::Private, console::U8::new(u8::rand(&mut rng) >> 1));
263                let second = U8::new(Mode::Private, console::U8::new(u8::rand(&mut rng) >> 1));
264                check_homomorphic_addition(pedersen, first, second, &mut rng);
265
266                // Sample two random unsigned integers, with the MSB set to 0.
267                let first = U16::<Circuit>::new(Mode::Private, console::U16::new(u16::rand(&mut rng) >> 1));
268                let second = U16::new(Mode::Private, console::U16::new(u16::rand(&mut rng) >> 1));
269                check_homomorphic_addition(pedersen, first, second, &mut rng);
270
271                // Sample two random unsigned integers, with the MSB set to 0.
272                let first = U32::<Circuit>::new(Mode::Private, console::U32::new(u32::rand(&mut rng) >> 1));
273                let second = U32::new(Mode::Private, console::U32::new(u32::rand(&mut rng) >> 1));
274                check_homomorphic_addition(pedersen, first, second, &mut rng);
275
276                // Sample two random unsigned integers, with the MSB set to 0.
277                let first = U64::<Circuit>::new(Mode::Private, console::U64::new(u64::rand(&mut rng) >> 1));
278                let second = U64::new(Mode::Private, console::U64::new(u64::rand(&mut rng) >> 1));
279                check_homomorphic_addition(pedersen, first, second, &mut rng);
280
281                // Sample two random unsigned integers, with the MSB set to 0.
282                let first = U128::<Circuit>::new(Mode::Private, console::U128::new(u128::rand(&mut rng) >> 1));
283                let second = U128::new(Mode::Private, console::U128::new(u128::rand(&mut rng) >> 1));
284                check_homomorphic_addition(pedersen, first, second, &mut rng);
285            }
286        }
287
288        // Check Pedersen128.
289        let pedersen128 = Pedersen128::constant(console::Pedersen128::setup("Pedersen128HomomorphismTest"));
290        check_pedersen_homomorphism(&pedersen128);
291    }
292}