commit_uncompressed.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 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 alphavm_circuit_types::environment::Circuit; 112 use alphavm_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 }