snarkvm_circuit_types_group/
lib.rs1#![forbid(unsafe_code)]
17#![allow(clippy::too_many_arguments)]
18#![cfg_attr(test, allow(clippy::assertions_on_result_states))]
19
20mod helpers;
21
22pub mod add;
23pub mod double;
24pub mod equal;
25pub mod mul;
26pub mod neg;
27pub mod sub;
28pub mod ternary;
29
30#[cfg(test)]
31use console::{TestRng, Uniform};
32#[cfg(test)]
33use snarkvm_circuit_environment::{assert_count, assert_output_mode, assert_scope, count, output_mode};
34
35use console::AffineCurve;
36use snarkvm_circuit_environment::prelude::*;
37use snarkvm_circuit_types_boolean::Boolean;
38use snarkvm_circuit_types_field::Field;
39use snarkvm_circuit_types_scalar::Scalar;
40
41#[derive(Clone)]
42pub struct Group<E: Environment> {
43 x: Field<E>,
44 y: Field<E>,
45}
46
47impl<E: Environment> GroupTrait<Scalar<E>> for Group<E> {}
48
49impl<E: Environment> Group<E> {
50 pub fn generator() -> Self {
52 Group::constant(console::Group::generator())
53 }
54}
55
56#[cfg(feature = "console")]
57impl<E: Environment> Inject for Group<E> {
58 type Primitive = console::Group<E::Network>;
59
60 fn new(mode: Mode, group: Self::Primitive) -> Self {
65 let x = Field::new(mode, group.to_x_coordinate());
67 let y = Field::new(mode, group.to_y_coordinate());
68 let point = Self { x, y };
70 point.enforce_in_group();
72 point
74 }
75}
76
77impl<E: Environment> Group<E> {
78 pub fn enforce_on_curve(&self) {
83 let a = Field::constant(console::Field::new(E::EDWARDS_A));
84 let d = Field::constant(console::Field::new(E::EDWARDS_D));
85
86 let x2 = self.x.square();
87 let y2 = self.y.square();
88
89 let first = y2;
90 let second = (d * &x2) - &Field::one();
91 let third = (a * x2) - Field::one();
92
93 E::enforce(|| (first, second, third));
95 }
96}
97
98impl<E: Environment> Group<E> {
99 pub fn enforce_in_group(&self) {
101 let self_witness = self.eject_value();
102
103 let mode = if self.eject_mode().is_constant() { Mode::Constant } else { Mode::Private };
113
114 let point_witness = self_witness.div_by_cofactor();
117 let point_x = Field::new(mode, point_witness.to_x_coordinate());
118 let point_y = Field::new(mode, point_witness.to_y_coordinate());
119 let point = Self { x: point_x, y: point_y };
120 point.enforce_on_curve();
121
122 debug_assert!(E::Affine::cofactor().len() == 1 && E::Affine::cofactor()[0] == 4);
124
125 let double_point = point.double();
128
129 double_point.enforce_double(self);
132 }
133
134 pub fn is_in_group(&self) -> Boolean<E> {
137 let order = E::ScalarField::modulus();
139 let order_bits_be = order.to_bits_be();
140 let mut order_bits_be_constants = Vec::with_capacity(order_bits_be.len());
141 for bit in order_bits_be.iter() {
142 order_bits_be_constants.push(Boolean::constant(*bit));
143 }
144 let self_times_order = order_bits_be_constants.mul(self);
146 self_times_order.is_zero()
148 }
149}
150
151#[cfg(feature = "console")]
152impl<E: Environment> Eject for Group<E> {
153 type Primitive = console::Group<E::Network>;
154
155 fn eject_mode(&self) -> Mode {
157 (&self.x, &self.y).eject_mode()
158 }
159
160 fn eject_value(&self) -> Self::Primitive {
162 console::Group::from_xy_coordinates_unchecked(self.x.eject_value(), self.y.eject_value())
163 }
164}
165
166#[cfg(feature = "console")]
167impl<E: Environment> Parser for Group<E> {
168 #[inline]
170 fn parse(string: &str) -> ParserResult<Self> {
171 let (string, group) = console::Group::parse(string)?;
173 let (string, mode) = opt(pair(tag("."), Mode::parse))(string)?;
175
176 match mode {
177 Some((_, mode)) => Ok((string, Group::new(mode, group))),
178 None => Ok((string, Group::new(Mode::Constant, group))),
179 }
180 }
181}
182
183#[cfg(feature = "console")]
184impl<E: Environment> FromStr for Group<E> {
185 type Err = Error;
186
187 #[inline]
189 fn from_str(string: &str) -> Result<Self> {
190 match Self::parse(string) {
191 Ok((remainder, object)) => {
192 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
194 Ok(object)
196 }
197 Err(error) => bail!("Failed to parse string. {error}"),
198 }
199 }
200}
201
202#[cfg(feature = "console")]
203impl<E: Environment> TypeName for Group<E> {
204 #[inline]
206 fn type_name() -> &'static str {
207 console::Group::<E::Network>::type_name()
208 }
209}
210
211#[cfg(feature = "console")]
212impl<E: Environment> Debug for Group<E> {
213 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
214 Display::fmt(self, f)
215 }
216}
217
218#[cfg(feature = "console")]
219impl<E: Environment> Display for Group<E> {
220 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
221 write!(f, "{}.{}", self.eject_value(), self.eject_mode())
222 }
223}
224
225impl<E: Environment> From<Group<E>> for LinearCombination<E::BaseField> {
226 fn from(group: Group<E>) -> Self {
227 From::from(&group)
228 }
229}
230
231impl<E: Environment> From<&Group<E>> for LinearCombination<E::BaseField> {
232 fn from(group: &Group<E>) -> Self {
233 group.to_x_coordinate().into()
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use super::*;
240 use snarkvm_circuit_environment::Circuit;
241
242 use core::str::FromStr;
243
244 const ITERATIONS: u64 = 128;
245
246 fn check_display(mode: Mode, point: console::Group<<Circuit as Environment>::Network>) {
248 let x = *point.to_x_coordinate();
249 let candidate = Group::<Circuit>::new(mode, point);
250 assert_eq!(format!("{x}{}.{mode}", Group::<Circuit>::type_name()), format!("{candidate}"));
251 }
252
253 #[test]
254 fn test_new() {
255 let mut rng = TestRng::default();
256
257 for i in 0..ITERATIONS {
259 let point = Uniform::rand(&mut rng);
261
262 Circuit::scope(format!("Constant {i}"), || {
263 let affine = Group::<Circuit>::new(Mode::Constant, point);
264 assert_eq!(point, affine.eject_value());
265 assert_scope!(10, 0, 0, 0);
266 });
267 }
268
269 for i in 0..ITERATIONS {
271 let point = Uniform::rand(&mut rng);
273
274 Circuit::scope(format!("Public {i}"), || {
275 let affine = Group::<Circuit>::new(Mode::Public, point);
276 assert_eq!(point, affine.eject_value());
277 assert_scope!(4, 2, 12, 13);
278 });
279 }
280
281 for i in 0..ITERATIONS {
283 let point = Uniform::rand(&mut rng);
285
286 Circuit::scope(format!("Private {i}"), || {
287 let affine = Group::<Circuit>::new(Mode::Private, point);
288 assert_eq!(point, affine.eject_value());
289 assert_scope!(4, 0, 14, 13);
290 });
291 }
292 }
293
294 #[test]
295 fn test_display() {
296 let mut rng = TestRng::default();
297
298 for _ in 0..ITERATIONS {
299 let point = Uniform::rand(&mut rng);
301
302 check_display(Mode::Constant, point);
304 check_display(Mode::Public, point);
306 check_display(Mode::Private, point);
308 }
309 }
310
311 #[test]
312 fn test_display_zero() {
313 let zero = console::Group::<<Circuit as Environment>::Network>::zero();
314
315 let candidate = Group::<Circuit>::new(Mode::Constant, zero);
317 assert_eq!("0group.constant", &format!("{candidate}"));
318
319 let candidate = Group::<Circuit>::new(Mode::Public, zero);
321 assert_eq!("0group.public", &format!("{candidate}"));
322
323 let candidate = Group::<Circuit>::new(Mode::Private, zero);
325 assert_eq!("0group.private", &format!("{candidate}"));
326 }
327
328 #[rustfmt::skip]
329 #[test]
330 fn test_parser() {
331 type Primitive = console::Group<<Circuit as Environment>::Network>;
332
333 let (_, candidate) = Group::<Circuit>::parse("2group").unwrap();
336 assert_eq!(Primitive::from_str("2group").unwrap(), candidate.eject_value());
337 assert!(candidate.is_constant());
338
339 let (_, candidate) = Group::<Circuit>::parse("2_group").unwrap();
340 assert_eq!(Primitive::from_str("2group").unwrap(), candidate.eject_value());
341 assert!(candidate.is_constant());
342
343 let (_, candidate) = Group::<Circuit>::parse("6124_8679069_09631996143302_21072113214281104_6555821040_573308695_4291647607832_31_group", ).unwrap();
344 assert_eq!(Primitive::from_str("6124867906909631996143302210721132142811046555821040573308695429164760783231group").unwrap(), candidate.eject_value());
345 assert!(candidate.is_constant());
346
347 let (_, candidate) = Group::<Circuit>::parse("2group.constant").unwrap();
348 assert_eq!(Primitive::from_str("2group").unwrap(), candidate.eject_value());
349 assert!(candidate.is_constant());
350
351 let (_, candidate) = Group::<Circuit>::parse("2_group.constant").unwrap();
352 assert_eq!(Primitive::from_str("2group").unwrap(), candidate.eject_value());
353 assert!(candidate.is_constant());
354
355 let (_, candidate) = Group::<Circuit>::parse("6124_8679069_09631996143302_21072113214281104_6555821040_573308695_4291647607832_31_group.constant", ).unwrap();
356 assert_eq!(Primitive::from_str("6124867906909631996143302210721132142811046555821040573308695429164760783231group").unwrap(), candidate.eject_value());
357 assert!(candidate.is_constant());
358
359 let (_, candidate) = Group::<Circuit>::parse("2group.public").unwrap();
362 assert_eq!(Primitive::from_str("2group").unwrap(), candidate.eject_value());
363 assert!(candidate.is_public());
364
365 let (_, candidate) = Group::<Circuit>::parse("2_group.public").unwrap();
366 assert_eq!(Primitive::from_str("2group").unwrap(), candidate.eject_value());
367 assert!(candidate.is_public());
368
369 let (_, candidate) = Group::<Circuit>::parse("6124_8679069_09631996143302_21072113214281104_6555821040_573308695_4291647607832_31_group.public", ).unwrap();
370 assert_eq!(Primitive::from_str("6124867906909631996143302210721132142811046555821040573308695429164760783231group").unwrap(), candidate.eject_value());
371 assert!(candidate.is_public());
372
373 let (_, candidate) = Group::<Circuit>::parse("2group.private").unwrap();
376 assert_eq!(Primitive::from_str("2group").unwrap(), candidate.eject_value());
377 assert!(candidate.is_private());
378
379 let (_, candidate) = Group::<Circuit>::parse("2_group.private").unwrap();
380 assert_eq!(Primitive::from_str("2group").unwrap(), candidate.eject_value());
381 assert!(candidate.is_private());
382
383 let (_, candidate) = Group::<Circuit>::parse("6124_8679069_09631996143302_21072113214281104_6555821040_573308695_4291647607832_31_group.private", ).unwrap();
384 assert_eq!(Primitive::from_str("6124867906909631996143302210721132142811046555821040573308695429164760783231group").unwrap(), candidate.eject_value());
385 assert!(candidate.is_private());
386
387 let mut rng = TestRng::default();
390
391 for mode in [Mode::Constant, Mode::Public, Mode::Private] {
392 for _ in 0..ITERATIONS {
393 let point = Uniform::rand(&mut rng);
394 let expected = Group::<Circuit>::new(mode, point);
395
396 let (_, candidate) = Group::<Circuit>::parse(&format!("{expected}")).unwrap();
397 assert_eq!(expected.eject_value(), candidate.eject_value());
398 assert_eq!(mode, candidate.eject_mode());
399 }
400 }
401 }
402
403 #[test]
404 fn test_is_in_group() {
405 type PrimitiveField = console::Field<<Circuit as Environment>::Network>;
406
407 let minus1_string = "8444461749428370424248824938781546531375899335154063827935233455917409239040field";
411 let q1x_string = "880904806456922042258150504921383618666682042621506879489field";
414 let q2x_string = "8444461749428370423367920132324624489117748830232680209268551413295902359552field";
415
416 let y1: Field<Circuit> = Field::new(Mode::Public, PrimitiveField::from_str("1field").unwrap());
418 let group0 = Group::<Circuit>::from_xy_coordinates_unchecked(Field::zero(), y1);
419 Circuit::scope("group0", || {
420 let group0_is_in_group = group0.is_in_group();
421 assert!(group0_is_in_group.eject_value());
422 assert_scope!(750, 0, 2253, 2253);
423 });
424 Circuit::reset();
425
426 let minus1: Field<Circuit> = Field::new(Mode::Public, PrimitiveField::from_str(minus1_string).unwrap());
429 let half0 = Group::<Circuit>::from_xy_coordinates_unchecked(Field::zero(), minus1);
430 Circuit::scope("half0", || {
431 let half0_is_not_in_group = !half0.is_in_group();
432 assert!(half0_is_not_in_group.eject_value());
433 assert_scope!(750, 0, 2253, 2253);
434 });
435 Circuit::reset();
436
437 let q1x: Field<Circuit> = Field::new(Mode::Public, PrimitiveField::from_str(q1x_string).unwrap());
438 let quarter1 = Group::<Circuit>::from_xy_coordinates_unchecked(q1x, Field::zero());
439 Circuit::scope("quarter1", || {
440 let quarter1_is_not_in_group = !quarter1.is_in_group();
441 assert!(quarter1_is_not_in_group.eject_value());
442 assert_scope!(750, 0, 2253, 2253);
443 });
444 Circuit::reset();
445
446 let q2x: Field<Circuit> = Field::new(Mode::Public, PrimitiveField::from_str(q2x_string).unwrap());
447 let quarter2 = Group::<Circuit>::from_xy_coordinates_unchecked(q2x, Field::zero());
448 Circuit::scope("quarter2", || {
449 let quarter2_is_not_in_group = !quarter2.is_in_group();
450 assert!(quarter2_is_not_in_group.eject_value());
451 assert_scope!(750, 0, 2253, 2253);
452 });
453 Circuit::reset();
454
455 fn check_is_in_group(mode: Mode, num_constants: u64, num_public: u64, num_private: u64, num_constraints: u64) {
456 let mut rng = TestRng::default();
457
458 for i in 0..ITERATIONS {
459 let point: console::Group<<Circuit as Environment>::Network> = Uniform::rand(&mut rng);
461
462 let x_coordinate = Field::new(mode, point.to_x_coordinate());
464
465 let element = Group::<Circuit>::from_x_coordinate(x_coordinate);
467
468 Circuit::scope(format!("{mode} {i}"), || {
469 let is_in_group = element.is_in_group();
470 assert!(is_in_group.eject_value());
471 assert_scope!(num_constants, num_public, num_private, num_constraints);
472 });
473 Circuit::reset();
474 }
475 }
476 check_is_in_group(Mode::Constant, 1752, 0, 0, 0);
477 check_is_in_group(Mode::Public, 750, 0, 2755, 2755);
478 check_is_in_group(Mode::Private, 750, 0, 2755, 2755);
479 }
480}