1use crate::{
10 gadgets::{from_emulated_field, EmulatedVariable, EmulationConfig, SerializableEmulatedStruct},
11 BoolVar, Circuit, CircuitError, PlonkCircuit,
12};
13use ark_ec::short_weierstrass::{Affine, SWCurveConfig};
14use ark_ff::PrimeField;
15use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
16use ark_std::{vec, vec::Vec};
17
18#[derive(Debug, Eq, PartialEq, Copy, Clone, CanonicalSerialize, CanonicalDeserialize)]
20pub struct SWPoint<F: PrimeField>(pub F, pub F, pub bool);
21
22impl<F: PrimeField> Default for SWPoint<F> {
23 fn default() -> Self {
24 Self(F::zero(), F::zero(), true)
25 }
26}
27
28impl<F, P> From<Affine<P>> for SWPoint<F>
29where
30 F: PrimeField,
31 P: SWCurveConfig<BaseField = F>,
32{
33 fn from(p: Affine<P>) -> Self {
34 SWPoint(p.x, p.y, p.infinity)
35 }
36}
37
38impl<E, F> SerializableEmulatedStruct<F> for SWPoint<E>
39where
40 E: EmulationConfig<F>,
41 F: PrimeField,
42{
43 fn serialize_to_native_elements(&self) -> Vec<F> {
44 let mut result = from_emulated_field(self.0);
45 result.extend(from_emulated_field(self.1));
46 result.push(if self.2 { F::one() } else { F::zero() });
47 result
48 }
49}
50
51#[derive(Debug, Clone)]
53pub struct EmulatedSWPointVariable<E: PrimeField>(
54 pub EmulatedVariable<E>,
55 pub EmulatedVariable<E>,
56 pub BoolVar,
57);
58
59impl<F: PrimeField> PlonkCircuit<F> {
60 pub fn emulated_sw_point_witness<E: EmulationConfig<F>>(
62 &self,
63 point_var: &EmulatedSWPointVariable<E>,
64 ) -> Result<SWPoint<E>, CircuitError> {
65 let x = self.emulated_witness(&point_var.0)?;
66 let y = self.emulated_witness(&point_var.1)?;
67 let infinity = self.witness(point_var.2 .0)? == F::one();
68 Ok(SWPoint(x, y, infinity))
69 }
70
71 pub fn create_emulated_sw_point_variable<E: EmulationConfig<F>>(
73 &mut self,
74 point: SWPoint<E>,
75 ) -> Result<EmulatedSWPointVariable<E>, CircuitError> {
76 let x = self.create_emulated_variable(point.0)?;
77 let y = self.create_emulated_variable(point.1)?;
78 let infinity = self.create_boolean_variable(point.2)?;
79 Ok(EmulatedSWPointVariable(x, y, infinity))
80 }
81
82 pub fn create_constant_emulated_sw_point_variable<E: EmulationConfig<F>>(
84 &mut self,
85 point: SWPoint<E>,
86 ) -> Result<EmulatedSWPointVariable<E>, CircuitError> {
87 let x = self.create_constant_emulated_variable(point.0)?;
88 let y = self.create_constant_emulated_variable(point.1)?;
89 let infinity = BoolVar(if point.2 { self.one() } else { self.zero() });
90 Ok(EmulatedSWPointVariable(x, y, infinity))
91 }
92
93 pub fn create_public_emulated_sw_point_variable<E: EmulationConfig<F>>(
95 &mut self,
96 point: SWPoint<E>,
97 ) -> Result<EmulatedSWPointVariable<E>, CircuitError> {
98 let x = self.create_public_emulated_variable(point.0)?;
99 let y = self.create_public_emulated_variable(point.1)?;
100 let infinity = self.create_public_boolean_variable(point.2)?;
101 Ok(EmulatedSWPointVariable(x, y, infinity))
102 }
103
104 pub fn binary_emulated_sw_point_vars_select<E: EmulationConfig<F>>(
109 &mut self,
110 b: BoolVar,
111 p0: &EmulatedSWPointVariable<E>,
112 p1: &EmulatedSWPointVariable<E>,
113 ) -> Result<EmulatedSWPointVariable<E>, CircuitError> {
114 let select_x = self.conditional_select_emulated(b, &p0.0, &p1.0)?;
115 let select_y = self.conditional_select_emulated(b, &p0.1, &p1.1)?;
116 let select_infinity = BoolVar(self.conditional_select(b, p0.2 .0, p1.2 .0)?);
117
118 Ok(EmulatedSWPointVariable::<E>(
119 select_x,
120 select_y,
121 select_infinity,
122 ))
123 }
124
125 pub fn enforce_emulated_sw_point_equal<E: EmulationConfig<F>>(
128 &mut self,
129 p0: &EmulatedSWPointVariable<E>,
130 p1: &EmulatedSWPointVariable<E>,
131 ) -> Result<(), CircuitError> {
132 self.enforce_emulated_var_equal(&p0.0, &p1.0)?;
133 self.enforce_emulated_var_equal(&p0.1, &p1.1)?;
134 self.enforce_equal(p0.2 .0, p1.2 .0)?;
135 Ok(())
136 }
137
138 pub fn is_emulated_sw_point_equal<E: EmulationConfig<F>>(
141 &mut self,
142 p0: &EmulatedSWPointVariable<E>,
143 p1: &EmulatedSWPointVariable<E>,
144 ) -> Result<BoolVar, CircuitError> {
145 let mut r0 = self.is_emulated_var_equal(&p0.0, &p1.0)?;
146 let r1 = self.is_emulated_var_equal(&p0.1, &p1.1)?;
147 let r2 = self.is_equal(p0.2 .0, p1.2 .0)?;
148 r0.0 = self.mul(r0.0, r1.0)?;
149 r0.0 = self.mul(r0.0, r2.0)?;
150 Ok(r0)
151 }
152
153 pub fn emulated_sw_ecc_add_gate<E: EmulationConfig<F>>(
197 &mut self,
198 p0: &EmulatedSWPointVariable<E>,
199 p1: &EmulatedSWPointVariable<E>,
200 p2: &EmulatedSWPointVariable<E>,
201 a: E,
202 ) -> Result<(), CircuitError> {
203 let eq_p1_p2 = self.is_emulated_sw_point_equal(p1, p2)?;
204 let eq_p0_p2 = self.is_emulated_sw_point_equal(p0, p2)?;
205 self.enforce_equal(p0.2 .0, eq_p1_p2.0)?;
207 self.enforce_equal(p1.2 .0, eq_p0_p2.0)?;
208
209 let infinity_mark = self.logic_or(p0.2, p1.2)?;
211 let non_infinity_mark = self.logic_neg(infinity_mark)?;
213
214 let non_inf_p2 = self.logic_neg(p2.2)?;
217 let eq_x0_x1 = self.is_emulated_var_equal(&p0.0, &p1.0)?;
218 let neq_x0_x1 = self.logic_neg(eq_x0_x1)?;
219 let eq_y0_y1 = self.is_emulated_var_equal(&p0.1, &p1.1)?;
220 let is_y0_zero = self.is_emulated_var_zero(&p0.1)?;
221 let not_y0_zero = self.logic_neg(is_y0_zero)?;
222 let t = self.logic_and(eq_y0_y1, not_y0_zero)?;
223 let t = self.logic_or(neq_x0_x1, t)?;
224 let t = self.logic_or(non_inf_p2, t)?;
225 self.logic_or_gate(infinity_mark, t)?;
226
227 let doubling_mark = self.mul(eq_x0_x1.0, eq_y0_y1.0)?;
230 let doubling_coef = self.mul(doubling_mark, non_infinity_mark.0)?;
231 let doubling_coef = self.mul(doubling_coef, not_y0_zero.0)?;
232 let mut v = vec![self.zero(); E::NUM_LIMBS];
235 v[0] = doubling_coef;
236 let doubling_coef = EmulatedVariable::<E>(v, core::marker::PhantomData);
237
238 let y0_times_2 = self.emulated_add(&p0.1, &p0.1)?;
240 let x0_plus_x1 = self.emulated_add(&p0.0, &p1.0)?;
241 let x0_plus_x1_plus_x2 = self.emulated_add(&p2.0, &x0_plus_x1)?;
242 let lhs = self.emulated_mul(&x0_plus_x1_plus_x2, &y0_times_2)?;
243 let lhs = self.emulated_mul(&lhs, &y0_times_2)?;
244 let s = self.emulated_mul(&p0.0, &p0.0)?;
246 let s = self.emulated_mul_constant(&s, E::from(3u64))?;
247 let s = self.emulated_add_constant(&s, a)?;
248 let rhs = self.emulated_mul(&s, &s)?;
249
250 let lhs = self.emulated_mul(&lhs, &doubling_coef)?;
251 let rhs = self.emulated_mul(&rhs, &doubling_coef)?;
252 self.enforce_emulated_var_equal(&lhs, &rhs)?;
253
254 let y2_plus_y0 = self.emulated_add(&p2.1, &p0.1)?;
256 let lhs = self.emulated_mul(&y2_plus_y0, &y0_times_2)?;
257 let x0_minus_x2 = self.emulated_sub(&p0.0, &p2.0)?;
258 let rhs = self.emulated_mul(&s, &x0_minus_x2)?;
259
260 let lhs = self.emulated_mul(&lhs, &doubling_coef)?;
261 let rhs = self.emulated_mul(&rhs, &doubling_coef)?;
262 self.enforce_emulated_var_equal(&lhs, &rhs)?;
263
264 let coef = self.mul(non_infinity_mark.0, neq_x0_x1.0)?;
266 let mut v = vec![self.zero(); E::NUM_LIMBS];
269 v[0] = coef;
270 let add_coef = EmulatedVariable::<E>(v, core::marker::PhantomData);
271
272 let x0_minus_x1 = self.emulated_sub(&p0.0, &p1.0)?;
274 let lhs = self.emulated_mul(&x0_minus_x1, &x0_minus_x1)?;
275 let lhs = self.emulated_mul(&lhs, &x0_plus_x1_plus_x2)?;
276 let y0_minus_y1 = self.emulated_sub(&p0.1, &p1.1)?;
277 let rhs = self.emulated_mul(&y0_minus_y1, &y0_minus_y1)?;
278
279 let lhs = self.emulated_mul(&lhs, &add_coef)?;
280 let rhs = self.emulated_mul(&rhs, &add_coef)?;
281 self.enforce_emulated_var_equal(&lhs, &rhs)?;
282
283 let lhs = self.emulated_mul(&x0_minus_x2, &y0_minus_y1)?;
285 let y0_plus_y2 = self.emulated_add(&p0.1, &p2.1)?;
286 let rhs = self.emulated_mul(&y0_plus_y2, &x0_minus_x1)?;
287
288 let lhs = self.emulated_mul(&lhs, &add_coef)?;
289 let rhs = self.emulated_mul(&rhs, &add_coef)?;
290 self.enforce_emulated_var_equal(&lhs, &rhs)?;
291
292 Ok(())
293 }
294
295 pub fn emulated_sw_ecc_add<E: EmulationConfig<F>>(
297 &mut self,
298 p0: &EmulatedSWPointVariable<E>,
299 p1: &EmulatedSWPointVariable<E>,
300 a: E,
301 ) -> Result<EmulatedSWPointVariable<E>, CircuitError> {
302 let x0 = self.emulated_witness(&p0.0)?;
303 let y0 = self.emulated_witness(&p0.1)?;
304 let infinity0 = self.witness(p0.2 .0)? == F::one();
305 let x1 = self.emulated_witness(&p1.0)?;
306 let y1 = self.emulated_witness(&p1.1)?;
307 let infinity1 = self.witness(p1.2 .0)? == F::one();
308 let p2 = if infinity0 {
309 SWPoint(x1, y1, infinity1)
310 } else if infinity1 {
311 SWPoint(x0, y0, infinity0)
312 } else if x0 == x1 && y0 == y1 {
313 if y0.is_zero() {
315 SWPoint(E::zero(), E::zero(), true)
316 } else {
317 let s = (x0 * x0 * E::from(3u64) + a) / (y0 + y0);
318 let x2 = s * s - x0 - x1;
319 let y2 = s * (x0 - x2) - y0;
320 SWPoint(x2, y2, false)
321 }
322 } else {
323 if x0 == x1 {
325 SWPoint(E::zero(), E::zero(), true)
326 } else {
327 let s = (y0 - y1) / (x0 - x1);
328 let x2 = s * s - x0 - x1;
329 let y2 = s * (x0 - x2) - y0;
330 SWPoint(x2, y2, false)
331 }
332 };
333 let p2 = self.create_emulated_sw_point_variable(p2)?;
334 self.emulated_sw_ecc_add_gate(p0, p1, &p2, a)?;
335 Ok(p2)
336 }
337}
338
339#[cfg(test)]
340mod tests {
341 use crate::{
342 gadgets::{ecc::conversion::*, EmulationConfig, SerializableEmulatedStruct},
343 Circuit, CircuitError, PlonkCircuit,
344 };
345 use ark_bls12_377::{g1::Config as Param377, Fq as Fq377};
346 use ark_bn254::{g1::Config as Param254, Fq as Fq254, Fr as Fr254};
347 use ark_ec::{
348 short_weierstrass::{Projective, SWCurveConfig},
349 CurveGroup, Group,
350 };
351 use ark_ff::{MontFp, PrimeField};
352 use ark_std::{UniformRand, Zero};
353
354 use super::{EmulatedSWPointVariable, SWPoint};
355
356 #[test]
357 fn test_emulated_sw_point_addition() {
358 let a: Fq377 = MontFp!("0");
359 test_emulated_sw_point_addition_helper::<Fq377, Fr254, Param377>(a);
360 let a: Fq254 = MontFp!("0");
361 test_emulated_sw_point_addition_helper::<Fq254, Fr254, Param254>(a);
362 }
363
364 fn ecc_add_and_check<E, F>(
365 circuit: &mut PlonkCircuit<F>,
366 p0: &EmulatedSWPointVariable<E>,
367 p1: &EmulatedSWPointVariable<E>,
368 a: E,
369 expected: &SWPoint<E>,
370 ) -> Result<EmulatedSWPointVariable<E>, CircuitError>
371 where
372 E: EmulationConfig<F> + SWToTEConParam,
373 F: PrimeField,
374 {
375 let result = circuit.emulated_sw_ecc_add(p0, p1, a)?;
376 assert_eq!(circuit.emulated_sw_point_witness(&result)?, *expected);
377 Ok(result)
378 }
379
380 fn test_emulated_sw_point_addition_helper<E, F, P>(a: E)
381 where
382 E: EmulationConfig<F> + SWToTEConParam,
383 F: PrimeField,
384 P: SWCurveConfig<BaseField = E>,
385 {
386 let mut rng = jf_utils::test_rng();
387 let neutral = Projective::<P>::zero().into_affine();
388 let p1 = Projective::<P>::rand(&mut rng).into_affine();
389 let p2 = Projective::<P>::rand(&mut rng).into_affine();
390 let expected = (p1 + p2).into_affine().into();
391 let wrong_result = (p1 + p2 + Projective::<P>::generator())
392 .into_affine()
393 .into();
394
395 let mut circuit = PlonkCircuit::<F>::new_ultra_plonk(20);
396
397 let var_p1 = circuit
398 .create_public_emulated_sw_point_variable(p1.into())
399 .unwrap();
400 let var_p2 = circuit
401 .create_emulated_sw_point_variable(p2.into())
402 .unwrap();
403 let var_neutral = circuit
404 .create_emulated_sw_point_variable(neutral.into())
405 .unwrap();
406 ecc_add_and_check::<E, F>(&mut circuit, &var_p1, &var_p2, a, &expected).unwrap();
407 ecc_add_and_check::<E, F>(&mut circuit, &var_p1, &var_neutral, a, &p1.into()).unwrap();
408 ecc_add_and_check::<E, F>(&mut circuit, &var_neutral, &var_p2, a, &p2.into()).unwrap();
409
410 let double_p1 = (p1 + p1).into_affine().into();
412 ecc_add_and_check::<E, F>(&mut circuit, &var_p1, &var_p1, a, &double_p1).unwrap();
413 ecc_add_and_check::<E, F>(&mut circuit, &var_neutral, &var_neutral, a, &neutral.into())
414 .unwrap();
415
416 let public_inputs: SWPoint<E> = p1.into();
417 let public_inputs = public_inputs.serialize_to_native_elements();
418 let wrong_inputs: SWPoint<E> = neutral.into();
419 let wrong_inputs = wrong_inputs.serialize_to_native_elements();
420 assert!(circuit.check_circuit_satisfiability(&public_inputs).is_ok());
421 assert!(circuit.check_circuit_satisfiability(&wrong_inputs).is_err());
422
423 let var_wrong_result = circuit
425 .create_emulated_sw_point_variable(wrong_result)
426 .unwrap();
427 circuit
428 .emulated_sw_ecc_add_gate(&var_p1, &var_p2, &var_wrong_result, a)
429 .unwrap();
430 assert!(circuit
431 .check_circuit_satisfiability(&public_inputs)
432 .is_err());
433 }
434}