/ circuit / algorithms / src / pedersen / commit_uncompressed.rs
commit_uncompressed.rs
  1  // Copyright (c) 2019-2025 Alpha-Delta Network Inc.
  2  // This file is part of the deltavm 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 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  
 37  impl<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  
 92  impl<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(test)]
109  mod tests {
110      use super::*;
111      use deltavm_circuit_types::environment::Circuit;
112      use deltavm_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  }