Skip to content

Commit 1ac3233

Browse files
chore: simplify add and sub (#84)
Co-authored-by: Tom French <[email protected]>
1 parent 54de613 commit 1ac3233

File tree

3 files changed

+174
-21
lines changed

3 files changed

+174
-21
lines changed

src/bigcurve_test.nr

Lines changed: 140 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ fn test_incomplete_add_with_hint() {
322322
}
323323

324324
#[test]
325-
fn test_add() {
325+
fn test_constrained_add() {
326326
unsafe {
327327
let P = BN254::one();
328328

@@ -380,6 +380,73 @@ fn test_add() {
380380
}
381381
}
382382

383+
#[test]
384+
unconstrained fn test_unconstrained_add() {
385+
let P = BN254::one();
386+
387+
// Q = 2P
388+
let Q = BN254 {
389+
x: BN254_Fq::from_limbs([
390+
0x7816a916871ca8d3c208c16d87cfd3,
391+
0x44e72e131a029b85045b68181585d9,
392+
0x0306,
393+
]),
394+
y: BN254_Fq::from_limbs([
395+
0xa6a449e3538fc7ff3ebf7a5a18a2c4,
396+
0x738c0e0a7c92e7845f96b2ae9c0a68,
397+
0x15ed,
398+
]),
399+
is_infinity: false,
400+
};
401+
402+
let result = BN254J::from(P.add(Q));
403+
404+
let P_j = BN254J::from(P);
405+
let Q_j = BN254J::from(Q);
406+
let expected = P_j.add(Q_j).0;
407+
408+
assert(result.eq(expected));
409+
}
410+
#[test]
411+
unconstrained fn test_unconstrained_add_one_side_infinity() {
412+
//Q + infinity and infinity + Q
413+
let Q = BN254::one();
414+
let P = BN254::point_at_infinity();
415+
let expected = BN254J::from(Q);
416+
let result = BN254J::from(Q.add(P));
417+
assert(result.eq(expected));
418+
let result = BN254J::from(P.add(Q));
419+
assert(result.eq(expected));
420+
}
421+
422+
#[test]
423+
unconstrained fn test_unconstrained_add_doubling() {
424+
// P + P
425+
let P = BN254::one();
426+
let Q = BN254::one();
427+
let P_j = BN254J::from(P);
428+
let result = BN254J::from(P.add(Q));
429+
let expected = P_j.dbl().0;
430+
assert(result.eq(expected));
431+
}
432+
433+
#[test]
434+
unconstrained fn test_unconstrained_add_infinity() {
435+
// P = -Q
436+
let P = BN254::one();
437+
let Q = P.neg();
438+
let result = BN254J::from(P.add(Q));
439+
let expected = BN254J::point_at_infinity();
440+
assert(result.eq(expected));
441+
442+
// both at infinity
443+
let P = BN254::point_at_infinity();
444+
let Q = BN254::point_at_infinity();
445+
let result = BN254J::from(Q.add(P));
446+
let expected = BN254J::point_at_infinity();
447+
assert(result.eq(expected));
448+
}
449+
383450
#[test]
384451
fn test_sub() {
385452
unsafe {
@@ -439,6 +506,78 @@ fn test_sub() {
439506
}
440507
}
441508

509+
#[test]
510+
unconstrained fn test_unconstrained_sub() {
511+
let P = BN254::one();
512+
513+
// Q = 2P
514+
let Q = BN254 {
515+
x: BN254_Fq::from_limbs([
516+
0x7816a916871ca8d3c208c16d87cfd3,
517+
0x44e72e131a029b85045b68181585d9,
518+
0x0306,
519+
]),
520+
y: BN254_Fq::from_limbs([
521+
0xa6a449e3538fc7ff3ebf7a5a18a2c4,
522+
0x738c0e0a7c92e7845f96b2ae9c0a68,
523+
0x15ed,
524+
]),
525+
is_infinity: false,
526+
};
527+
528+
let result = BN254J::from(P.sub(Q));
529+
530+
let P_j = BN254J::from(P);
531+
let Q_j = BN254J::from(Q);
532+
let expected = P_j.sub(Q_j).0;
533+
534+
assert(result.eq(expected));
535+
}
536+
537+
#[test]
538+
unconstrained fn test_unconstrained_sub_doubling() {
539+
// doubling
540+
let P = BN254::one();
541+
let Q = BN254::one();
542+
543+
let result = BN254J::from(P.sub(Q.neg()));
544+
let P_j = BN254J::from(P);
545+
let expected = P_j.dbl().0;
546+
assert(result.eq(expected));
547+
}
548+
549+
#[test]
550+
unconstrained fn test_unconstrained_sub_infinity() {
551+
// both at infinity
552+
let P = BN254::one();
553+
let Q = BN254::one();
554+
let result = BN254J::from(P.sub(Q));
555+
let expected = BN254J::point_at_infinity();
556+
assert(result.eq(expected));
557+
// both infinity
558+
let P = BN254::point_at_infinity();
559+
let Q = BN254::point_at_infinity();
560+
let result = BN254J::from(Q.sub(P));
561+
let expected = BN254J::point_at_infinity();
562+
assert(result.eq(expected));
563+
}
564+
565+
#[test]
566+
unconstrained fn test_unconstrained_sub_one_side_infinity() {
567+
let P = BN254::point_at_infinity();
568+
let Q = BN254::one();
569+
570+
// lhs infinity
571+
let result = BN254J::from(P.sub(Q));
572+
let expected = BN254J::from(Q.neg());
573+
assert(result.eq(expected));
574+
575+
// rhs infinity
576+
let result = BN254J::from(Q.sub(P));
577+
let expected = expected.neg();
578+
assert(result.eq(expected));
579+
}
580+
442581
#[test]
443582
fn test_make_table() {
444583
unsafe {

src/curve_jac.nr

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ where
304304
let y_equal_predicate = S2.eq(S1);
305305
let lhs_infinity = self.is_infinity;
306306
let rhs_infinity = p2.is_infinity;
307+
307308
let double_predicate =
308309
x_equal_predicate & y_equal_predicate & !lhs_infinity & !rhs_infinity;
309310
let add_predicate = !x_equal_predicate & !lhs_infinity & !rhs_infinity;
@@ -317,9 +318,13 @@ where
317318
} else if (infinity_predicate) {
318319
result = (CurveJ::point_at_infinity(), JTranscript::new());
319320
} else if (lhs_infinity & !rhs_infinity) {
320-
result = (p2, JTranscript::new());
321+
let new_transcript =
322+
JTranscript { x3: p2.x, y3: p2.y, z3: p2.z, lambda_numerator: B::zero() };
323+
result = (p2, new_transcript);
321324
} else if (rhs_infinity & !lhs_infinity) {
322-
result = (self, JTranscript::new());
325+
let new_transcript =
326+
JTranscript { x3: self.x, y3: self.y, z3: self.z, lambda_numerator: B::zero() };
327+
result = (self, new_transcript);
323328
}
324329
result
325330
// let (_, PP): (B, B ) = B::compute_quadratic_expression([[U2, U1]], [[false, true]], [[U2, U1]], [[false, true]], [], []);

src/lib.nr

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -184,12 +184,16 @@ pub comptime fn derive_curve_impl(
184184
// Expensive witness generation! Avoid if possible
185185
impl std::ops::Add for $typ {
186186
fn add(self, other: Self) -> Self {
187-
let lhsJ = $CurveJ::<$field_type, $typ>::from(self);
188-
let rhsJ = $CurveJ::<$field_type, $typ>::from(other);
189-
190-
let transcript = unsafe { $AffineTranscript::from_j(lhsJ.add(rhsJ).1) };
191-
192-
$crate::add_with_hint::<$field_type, $typ>(self,other, transcript)
187+
let lhsJ = $crate::curve_jac::CurveJ::<$field_type, $typ>::from(self);
188+
let rhsJ = $crate::curve_jac::CurveJ::<$field_type, $typ>::from(other);
189+
let (result_jac, j_transcript) = unsafe { lhsJ.add(rhsJ) };
190+
let transcript = unsafe { $crate::curve_jac::AffineTranscript::from_j(j_transcript) };
191+
if std::runtime::is_unconstrained() {
192+
$typ::from_coordinates(transcript.x3, transcript.y3, result_jac.is_infinity)
193+
194+
} else {
195+
$crate::add_with_hint::<$field_type, $typ>(self, other, transcript)
196+
}
193197
}
194198
}
195199

@@ -204,12 +208,17 @@ pub comptime fn derive_curve_impl(
204208

205209
impl std::ops::Sub for $typ {
206210
fn sub(self, other: Self) -> Self {
207-
let lhsJ = $CurveJ::<$field_type, $typ>::from(self);
208-
let rhsJ = $CurveJ::<$field_type, $typ>::from(other);
209-
210-
let transcript = unsafe { $AffineTranscript::from_j(lhsJ.sub(rhsJ).1) };
211-
212-
$crate::sub_with_hint::<$field_type, $typ>(self, other, transcript)
211+
let lhsJ = $crate::curve_jac::CurveJ::<$field_type, $typ>::from(self);
212+
let rhsJ = $crate::curve_jac::CurveJ::<$field_type, $typ>::from(other);
213+
let (result_jac, j_transcript) = unsafe { lhsJ.sub(rhsJ) };
214+
215+
// Convert back to affine coordinates using the transcript
216+
let transcript = unsafe { $crate::curve_jac::AffineTranscript::from_j(j_transcript) };
217+
if std::runtime::is_unconstrained() {
218+
$typ::from_coordinates(transcript.x3, transcript.y3, result_jac.is_infinity)
219+
} else {
220+
$crate::sub_with_hint::<$field_type, $typ>(self, other, transcript)
221+
}
213222
}
214223
}
215224

@@ -549,10 +558,10 @@ pub(crate) fn add_with_hint<B: BigNum, P: BigCurve<B>>(
549558

550559
// If we are skipping the evaluation of a group operation (x2 == x1, y2 == -y1 OR any input points are at infinity),
551560
// set input operands to 0!
552-
let (x1, y1, x2, y2) = if evaluate_group_operation_predicate {
553-
(x1, y1, x2, y2)
561+
let (x1, y1, x2, y2, x3, y3) = if evaluate_group_operation_predicate {
562+
(x1, y1, x2, y2, x3, y3)
554563
} else {
555-
(B::zero(), B::zero(), B::zero(), B::zero())
564+
(B::zero(), B::zero(), B::zero(), B::zero(), B::zero(), B::zero())
556565
};
557566

558567
// lambda * 2y - 3x * x = 0
@@ -678,10 +687,10 @@ pub(crate) fn sub_with_hint<B: BigNum, P: BigCurve<B>>(
678687

679688
// If we are skipping the evaluation of a group operation (x2 == x1, y2 == -y1 OR any input points are at infinity),
680689
// set input operands to 0!
681-
let (x1, y1, x2, y2) = if evaluate_group_operation_predicate {
682-
(x1, y1, x2, y2)
690+
let (x1, y1, x2, y2, x3, y3) = if evaluate_group_operation_predicate {
691+
(x1, y1, x2, y2, x3, y3)
683692
} else {
684-
(B::zero(), B::zero(), B::zero(), B::zero())
693+
(B::zero(), B::zero(), B::zero(), B::zero(), B::zero(), B::zero())
685694
};
686695

687696
// lambda * 2y - 3x*x = 0

0 commit comments

Comments
 (0)