1use crate::{
8 gadgets::ecc::{MultiScalarMultiplicationCircuit, PointVariable},
9 BoolVar, Circuit, CircuitError, PlonkCircuit, Variable,
10};
11use ark_ec::{
12 twisted_edwards::{Projective, TECurveConfig},
13 CurveGroup,
14};
15use ark_ff::{PrimeField, Zero};
16use jf_utils::field_switching;
17use num_bigint::{BigInt, BigUint};
18
19use super::TEPoint;
20
21const COEFF_B: [u8; 32] = [
25 180, 16, 37, 23, 77, 1, 15, 238, 214, 244, 154, 13, 119, 18, 167, 46, 136, 26, 81, 99, 58, 13,
26 240, 97, 165, 38, 132, 130, 139, 242, 201, 82,
27];
28
29const COEFF_C: [u8; 32] = [
30 61, 11, 101, 223, 108, 128, 92, 81, 233, 244, 54, 255, 207, 171, 86, 132, 7, 209, 23, 108, 253,
31 110, 124, 169, 195, 87, 84, 134, 207, 36, 198, 108,
32];
33const LAMBDA: [u8; 32] = [
35 5, 223, 131, 135, 64, 33, 61, 209, 110, 5, 165, 112, 185, 157, 196, 207, 43, 199, 56, 43, 86,
36 73, 248, 237, 147, 164, 57, 74, 220, 243, 180, 19,
37];
38const LAMBDA_1: [u8; 32] = [
40 5, 223, 131, 135, 64, 33, 61, 209, 110, 5, 165, 112, 185, 157, 196, 207, 0, 0, 0, 0, 0, 0, 0,
41 0, 0, 0, 0, 0, 0, 0, 0, 0,
42];
43const LAMBDA_2: [u8; 32] = [
46 43, 199, 56, 43, 86, 73, 248, 237, 147, 164, 57, 74, 220, 243, 180, 19, 0, 0, 0, 0, 0, 0, 0, 0,
47 0, 0, 0, 0, 0, 0, 0, 0,
48];
49const R1: [u8; 32] = [
52 225, 231, 118, 40, 181, 6, 253, 116, 113, 4, 25, 116, 0, 135, 143, 255, 0, 0, 0, 0, 0, 0, 0, 0,
53 0, 0, 0, 0, 0, 0, 0, 0,
54];
55const R2: [u8; 32] = [
58 0, 118, 104, 2, 2, 118, 206, 12, 82, 95, 103, 202, 212, 105, 251, 28, 0, 0, 0, 0, 0, 0, 0, 0,
59 0, 0, 0, 0, 0, 0, 0, 0,
60];
61
62const COEFF_N11: [u8; 32] = [
63 31, 24, 137, 151, 74, 249, 2, 75, 142, 146, 230, 75, 0, 226, 95, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0,
64 0, 0, 0, 0, 0, 0, 0,
65];
66
67const COEFF_N12: [u8; 32] = [
68 68, 31, 214, 35, 26, 89, 226, 248, 93, 143, 94, 229, 238, 179, 20, 8, 0, 0, 0, 0, 0, 0, 0, 0,
69 0, 0, 0, 0, 0, 0, 0, 0,
70];
71
72const COEFF_N21: [u8; 32] = [
73 136, 62, 172, 71, 52, 178, 196, 241, 187, 30, 189, 202, 221, 103, 41, 16, 0, 0, 0, 0, 0, 0, 0,
74 0, 0, 0, 0, 0, 0, 0, 0, 0,
75];
76const COEFF_N22: [u8; 32] = [
77 194, 207, 237, 144, 106, 13, 250, 41, 227, 113, 50, 40, 0, 165, 47, 170, 0, 118, 104, 2, 2,
78 118, 206, 12, 82, 95, 103, 202, 212, 105, 251, 28,
79];
80
81impl<F> PlonkCircuit<F>
83where
84 F: PrimeField,
85{
86 pub fn glv_mul<P: TECurveConfig<BaseField = F>>(
89 &mut self,
90 scalar: Variable,
91 base: &PointVariable,
92 ) -> Result<PointVariable, CircuitError> {
93 self.check_var_bound(scalar)?;
94 self.check_point_var_bound(base)?;
95
96 let (s1_var, s2_var, s2_sign_var) =
97 scalar_decomposition_gate::<P::BaseField, P::ScalarField>(self, &scalar)?;
98
99 let endo_base_var = endomorphism_circuit::<_, P>(self, base)?;
100 multi_scalar_mul_circuit::<_, P>(self, base, s1_var, &endo_base_var, s2_var, s2_sign_var)
101 }
102}
103
104fn multi_scalar_mul_circuit<F, P>(
106 circuit: &mut PlonkCircuit<F>,
107 base: &PointVariable,
108 scalar_1: Variable,
109 endo_base: &PointVariable,
110 scalar_2: Variable,
111 scalar_2_sign_var: BoolVar,
112) -> Result<PointVariable, CircuitError>
113where
114 F: PrimeField,
115 P: TECurveConfig<BaseField = F>,
116{
117 let endo_base_neg = circuit.inverse_point(endo_base)?;
118 let endo_base =
119 circuit.binary_point_vars_select(scalar_2_sign_var, endo_base, &endo_base_neg)?;
120
121 MultiScalarMultiplicationCircuit::<F, P>::msm_with_var_scalar_length(
122 circuit,
123 &[*base, endo_base],
124 &[scalar_1, scalar_2],
125 128,
126 )
127}
128
129fn endomorphism<F, P>(base: &TEPoint<F>) -> TEPoint<F>
131where
132 F: PrimeField,
133 P: TECurveConfig<BaseField = F>,
134{
135 let x = base.get_x();
136 let y = base.get_y();
137 let b = F::from_le_bytes_mod_order(COEFF_B.as_ref());
138 let c = F::from_le_bytes_mod_order(COEFF_C.as_ref());
139
140 let xy = x * y;
141 let y_square = y * y;
142 let f_y = c * (F::one() - y_square);
143 let g_y = b * (y_square + b);
144 let h_y = y_square - b;
145
146 Projective::<P>::new(f_y * h_y, g_y * xy, F::one(), h_y * xy)
147 .into_affine()
148 .into()
149}
150
151fn endomorphism_circuit<F, P>(
153 circuit: &mut PlonkCircuit<F>,
154 point_var: &PointVariable,
155) -> Result<PointVariable, CircuitError>
156where
157 F: PrimeField,
158 P: TECurveConfig<BaseField = F>,
159{
160 let base = circuit.point_witness(point_var)?;
161 let endo_point = endomorphism::<_, P>(&base);
162 let endo_point_var = circuit.create_point_variable(endo_point)?;
163
164 let b = F::from_le_bytes_mod_order(COEFF_B.as_ref());
165 let c = F::from_le_bytes_mod_order(COEFF_C.as_ref());
166 let b_square = b * b;
167
168 let x_var = point_var.get_x();
169 let y_var = point_var.get_y();
170
171 let xy_var = circuit.mul(x_var, y_var)?;
173
174 let wire = [y_var, y_var, circuit.zero(), circuit.zero()];
176 let coeff = [F::zero(), F::zero(), F::zero(), F::zero()];
177 let q_mul = [-c, F::zero()];
178 let q_c = c;
179 let f_y_var = circuit.gen_quad_poly(&wire, &coeff, &q_mul, q_c)?;
180
181 let wire = [y_var, y_var, circuit.zero(), circuit.zero()];
183 let coeff = [F::zero(), F::zero(), F::zero(), F::zero()];
184 let q_mul = [b, F::zero()];
185 let q_c = b_square;
186 let g_y_var = circuit.gen_quad_poly(&wire, &coeff, &q_mul, q_c)?;
187
188 let wire = [y_var, y_var, circuit.zero(), circuit.zero()];
190 let coeff = [F::zero(), F::zero(), F::zero(), F::zero()];
191 let q_mul = [F::one(), F::zero()];
192 let q_c = -b;
193 let h_y_var = circuit.gen_quad_poly(&wire, &coeff, &q_mul, q_c)?;
194
195 circuit.mul_gate(endo_point_var.get_x(), xy_var, f_y_var)?;
197 circuit.mul_gate(endo_point_var.get_y(), h_y_var, g_y_var)?;
199
200 Ok(endo_point_var)
201}
202
203fn scalar_decomposition<F: PrimeField>(scalar: &F) -> (F, F, bool) {
208 let scalar_z: BigUint = (*scalar).into();
209
210 let tmp = F::from_le_bytes_mod_order(COEFF_N11.as_ref());
211 let n11: BigUint = tmp.into();
212
213 let tmp = F::from_le_bytes_mod_order(COEFF_N12.as_ref());
214 let n12: BigUint = tmp.into();
215
216 let tmp = F::from_le_bytes_mod_order(COEFF_N21.as_ref());
217 let n21: BigUint = tmp.into();
218
219 let tmp = F::from_le_bytes_mod_order(COEFF_N22.as_ref());
220 let n22: BigUint = tmp.into();
221
222 let r: BigUint = F::MODULUS.into();
223 let r_over_2 = &r / BigUint::from(2u8);
224
225 let beta_1 = &scalar_z * &n11;
227 let beta_2 = &scalar_z * &n12;
228
229 let beta_1 = &beta_1 / &r;
230 let beta_2 = &beta_2 / &r;
231
232 let b1: BigUint = &beta_1 * &n11 + &beta_2 * &n21;
234 let b2: BigUint = (&beta_1 * &n12 + &beta_2 * &n22) % r;
235
236 let k1 = F::from(scalar_z - b1);
237 let is_k2_pos = b2 < r_over_2;
238
239 let k2 = if is_k2_pos { F::from(b2) } else { -F::from(b2) };
240
241 (k1, k2, is_k2_pos)
242}
243
244macro_rules! fq_to_big_int {
245 ($fq: expr) => {
246 <BigInt as From<BigUint>>::from($fq.into_bigint().into())
247 };
248}
249
250macro_rules! int_to_fq {
251 ($in: expr) => {
252 F::from_le_bytes_mod_order(&$in.to_bytes_le().1)
253 };
254}
255
256#[allow(clippy::type_complexity)]
267fn scalar_decomposition_gate<F, S>(
268 circuit: &mut PlonkCircuit<F>,
269 s_var: &Variable,
270) -> Result<(Variable, Variable, BoolVar), CircuitError>
271where
272 F: PrimeField,
273 S: PrimeField,
274{
275 let two_to_128 = BigInt::from(2u64).pow(128);
337
338 let s = circuit.witness(*s_var)?;
340 let s_int = fq_to_big_int!(s);
341 let s_fr = field_switching::<_, S>(&s);
342
343 let lambda = F::from_le_bytes_mod_order(LAMBDA.as_ref());
345 let lambda_1 = F::from_le_bytes_mod_order(LAMBDA_1.as_ref());
346
347 let lambda_int = fq_to_big_int!(lambda);
348 let lambda_1_int = fq_to_big_int!(lambda_1);
349 let lambda_2 = F::from_le_bytes_mod_order(LAMBDA_2.as_ref());
350
351 let (k1, k2, is_k2_positive) = scalar_decomposition(&s_fr);
353 let k1_int = fq_to_big_int!(k1);
354 let k2_int = fq_to_big_int!(k2);
355 let k2_sign = if is_k2_positive {
356 BigInt::from(1)
357 } else {
358 BigInt::from(-1)
359 };
360 let k2_with_sign = &k2_int * &k2_sign;
361
362 let fr_order_uint: BigUint = S::MODULUS.into();
364 let fr_order_int: BigInt = fr_order_uint.into();
365 let r1 = F::from_le_bytes_mod_order(R1.as_ref());
366 let r1_int = fq_to_big_int!(r1);
367 let r2 = F::from_le_bytes_mod_order(R2.as_ref());
368
369 let mut t_int = (&lambda_int * &k2_with_sign + &s_int - &k1_int) / &fr_order_int;
371 let t_int_sign = if t_int < BigInt::zero() {
372 t_int = -t_int;
373 BigInt::from(-1)
374 } else {
375 BigInt::from(1)
376 };
377 let t_int_with_sign = &t_int * &t_int_sign;
378
379 let tmp_int = &lambda_1_int * &k2_with_sign + &s_int - &t_int_with_sign * &r1_int - &k1_int;
381 let tmp2_int = &tmp_int / &two_to_128;
382
383 #[cfg(test)]
384 {
385 use ark_ff::BigInteger;
386
387 let fq_uint: BigUint = F::MODULUS.into();
388 let fq_int: BigInt = fq_uint.into();
389
390 let tmp1_int = &tmp_int % &two_to_128;
391
392 let lambda_2_int = fq_to_big_int!(lambda_2);
393 let r2_int = fq_to_big_int!(r2);
394 assert_eq!(
397 &s_int + &lambda_int * &k2_with_sign,
398 &k1_int + &t_int_with_sign * &fr_order_int
399 );
400
401 assert_eq!(
406 &lambda_1_int * &k2_with_sign + &s_int - &t_int_with_sign * &r1_int - &k1_int
407 + &two_to_128 * (&lambda_2_int * &k2_with_sign - &t_int_with_sign * &r2_int),
408 BigInt::zero()
409 );
410
411 let k1_bits = get_bits(&k1.into_bigint().to_bits_le());
414 let k2_bits = get_bits(&k1.into_bigint().to_bits_le());
415
416 assert!(k1_bits < 128, "k1 bits {}", k1_bits);
417 assert!(k2_bits < 128, "k2 bits {}", k1_bits);
418
419 assert!(tmp1_int == BigInt::from(0));
423 let tmp2_fq = F::from_le_bytes_mod_order(&tmp2_int.to_bytes_le().1);
424 let tmp2_bits = get_bits(&tmp2_fq.into_bigint().to_bits_le());
425 assert!(tmp1_int == BigInt::from(0));
426 assert!(tmp2_bits < 128, "tmp2 bits {}", tmp2_bits);
427
428 assert_eq!(
431 &tmp1_int + &two_to_128 * &tmp2_int,
432 &lambda_1_int * &k2_with_sign + &s_int - &t_int_with_sign * &r1_int - &k1_int
433 );
434 assert!(&tmp_int + &t_int_with_sign * &r1_int + &k1_int < fq_int);
435
436 assert!(&lambda_1_int * &k2_int + &s_int < fq_int);
437
438 assert_eq!(
440 &tmp2_int + &lambda_2_int * &k2_with_sign,
441 &t_int_with_sign * &r2_int
442 );
443
444 assert!(k1_int >= BigInt::zero());
446 assert!(k2_int >= BigInt::zero());
447 assert!(t_int >= BigInt::zero());
448 assert!(tmp_int >= BigInt::zero());
449 assert!(tmp2_int >= BigInt::zero());
450
451 assert_eq!(t_int_sign, k2_sign);
453 }
454
455 let two_to_128 = F::from(BigUint::from(2u64).pow(128));
459
460 let k1_var = circuit.create_variable(int_to_fq!(k1_int))?;
461 let k2_var = circuit.create_variable(int_to_fq!(k2_int))?;
462 let k2_sign_var = circuit.create_boolean_variable(is_k2_positive)?;
463
464 let t_var = circuit.create_variable(int_to_fq!(t_int))?;
465
466 let tmp_var = circuit.create_variable(int_to_fq!(tmp_int))?;
467
468 let tmp2_var = circuit.create_variable(int_to_fq!(tmp2_int))?;
469
470 circuit.enforce_in_range(k1_var, 128)?;
476 circuit.enforce_in_range(k2_var, 128)?;
477
478 circuit.mul_constant_gate(tmp2_var, two_to_128, tmp_var)?;
482 circuit.enforce_in_range(tmp2_var, 128)?;
483
484 let k2_is_pos_sat = {
490 let left_wire = [tmp_var, t_var, k1_var, circuit.zero()];
493 let left_coeff = [F::one(), r1, F::one(), F::zero()];
494 let left_var = circuit.lc(&left_wire, &left_coeff)?;
495
496 let right_wire = [k2_var, *s_var, circuit.zero(), circuit.zero()];
497 let right_coeff = [lambda_1, F::one(), F::zero(), F::zero()];
498 let right_var = circuit.lc(&right_wire, &right_coeff)?;
499
500 circuit.is_equal(left_var, right_var)?
501 };
502
503 let k2_is_neg_sat = {
504 let left_wire = [k2_var, tmp_var, k1_var, circuit.zero()];
507 let left_coeff = [lambda_1, F::one(), F::one(), F::zero()];
508 let left_var = circuit.lc(&left_wire, &left_coeff)?;
509
510 let right_wire = [*s_var, t_var, circuit.zero(), circuit.zero()];
511 let right_coeff = [F::one(), r1, F::zero(), F::zero()];
512 let right_var = circuit.lc(&right_wire, &right_coeff)?;
513 circuit.is_equal(left_var, right_var)?
514 };
515
516 let sat =
518 circuit.conditional_select(k2_sign_var, k2_is_neg_sat.into(), k2_is_pos_sat.into())?;
519 circuit.enforce_true(sat)?;
520
521 let k2_is_pos_sat = {
524 let left_wire = [tmp2_var, k2_var, circuit.zero(), circuit.zero()];
527 let left_coeff = [F::one(), lambda_2, F::zero(), F::zero()];
528 let left_var = circuit.lc(&left_wire, &left_coeff)?;
529
530 let right_var = circuit.mul_constant(t_var, &r2)?;
531
532 circuit.is_equal(left_var, right_var)?
533 };
534
535 let k2_is_neg_sat = {
536 let left_wire = [tmp2_var, t_var, circuit.zero(), circuit.zero()];
539 let left_coeff = [F::one(), r2, F::zero(), F::zero()];
540 let left_var = circuit.lc(&left_wire, &left_coeff)?;
541
542 let right_var = circuit.mul_constant(k2_var, &lambda_2)?;
543
544 circuit.is_equal(left_var, right_var)?
545 };
546
547 let sat =
549 circuit.conditional_select(k2_sign_var, k2_is_neg_sat.into(), k2_is_pos_sat.into())?;
550 circuit.enforce_true(sat)?;
551
552 Ok((k1_var, k2_var, k2_sign_var))
554}
555
556#[cfg(test)]
557fn get_bits(a: &[bool]) -> u16 {
559 let mut res = 256;
560 for e in a.iter().rev() {
561 if !e {
562 res -= 1;
563 } else {
564 return res;
565 }
566 }
567 res
568}
569
570#[cfg(test)]
571mod tests {
572 use super::*;
573 use ark_ec::{
574 twisted_edwards::{Affine, TECurveConfig as Config},
575 CurveConfig,
576 };
577 use ark_ed_on_bls12_381_bandersnatch::{EdwardsAffine, EdwardsConfig, Fq, Fr};
578 use ark_ff::{BigInteger, MontFp, One, UniformRand};
579 use jf_utils::{fr_to_fq, test_rng};
580
581 #[test]
582 fn test_glv() -> Result<(), CircuitError> {
583 test_glv_helper::<Fq, EdwardsConfig>()
584 }
585
586 fn test_glv_helper<F, P>() -> Result<(), CircuitError>
587 where
588 F: PrimeField,
589 P: Config<BaseField = F>,
590 {
591 let mut rng = jf_utils::test_rng();
592
593 for _ in 0..100 {
594 {
595 let mut base = Affine::<P>::rand(&mut rng);
596 let s = P::ScalarField::rand(&mut rng);
597 let mut circuit: PlonkCircuit<F> = PlonkCircuit::new_turbo_plonk();
598
599 let s_var = circuit.create_variable(fr_to_fq::<F, P>(&s))?;
600 let base_var = circuit.create_point_variable(TEPoint::from(base))?;
601 base = (base * s).into();
602 let result = circuit.variable_base_scalar_mul::<P>(s_var, &base_var)?;
603 assert_eq!(TEPoint::from(base), circuit.point_witness(&result)?);
604
605 assert!(circuit.check_circuit_satisfiability(&[]).is_ok());
607 }
608 {
609 let mut base = Affine::<P>::rand(&mut rng);
610 let s = P::ScalarField::rand(&mut rng);
611 let mut circuit: PlonkCircuit<F> = PlonkCircuit::new_ultra_plonk(16);
612
613 let s_var = circuit.create_variable(fr_to_fq::<F, P>(&s))?;
614 let base_var = circuit.create_point_variable(TEPoint::from(base))?;
615 base = (base * s).into();
616 let result = circuit.variable_base_scalar_mul::<P>(s_var, &base_var)?;
617 assert_eq!(TEPoint::from(base), circuit.point_witness(&result)?);
618
619 assert!(circuit.check_circuit_satisfiability(&[]).is_ok());
621 }
622
623 {
624 let mut base = Affine::<P>::rand(&mut rng);
625 let s = P::ScalarField::rand(&mut rng);
626 let mut circuit: PlonkCircuit<F> = PlonkCircuit::new_turbo_plonk();
627
628 let s_var = circuit.create_variable(fr_to_fq::<F, P>(&s))?;
629 let base_var = circuit.create_point_variable(TEPoint::from(base))?;
630 base = (base * s).into();
631 let result = circuit.glv_mul::<P>(s_var, &base_var)?;
632 assert_eq!(TEPoint::from(base), circuit.point_witness(&result)?);
633
634 assert!(circuit.check_circuit_satisfiability(&[]).is_ok());
636 }
637
638 {
639 let mut base = Affine::<P>::rand(&mut rng);
640 let s = P::ScalarField::rand(&mut rng);
641 let mut circuit: PlonkCircuit<F> = PlonkCircuit::new_ultra_plonk(16);
642
643 let s_var = circuit.create_variable(fr_to_fq::<F, P>(&s))?;
644 let base_var = circuit.create_point_variable(TEPoint::from(base))?;
645 base = (base * s).into();
646 let result = circuit.glv_mul::<P>(s_var, &base_var)?;
647 assert_eq!(TEPoint::from(base), circuit.point_witness(&result)?);
648
649 assert!(circuit.check_circuit_satisfiability(&[]).is_ok());
651 }
652 }
653 Ok(())
654 }
655
656 #[test]
657 fn test_endomorphism() {
658 let base_point = EdwardsAffine::new_unchecked(
659 MontFp!(
660 "29627151942733444043031429156003786749302466371339015363120350521834195802525"
661 ),
662 MontFp!(
663 "27488387519748396681411951718153463804682561779047093991696427532072116857978"
664 ),
665 );
666 let endo_point = EdwardsAffine::new_unchecked(
667 MontFp!("3995099504672814451457646880854530097687530507181962222512229786736061793535"),
668 MontFp!(
669 "33370049900732270411777328808452912493896532385897059012214433666611661340894"
670 ),
671 );
672 let base_point: TEPoint<Fq> = base_point.into();
673 let endo_point: TEPoint<Fq> = endo_point.into();
674
675 let t = endomorphism::<_, EdwardsConfig>(&base_point);
676 assert_eq!(t, endo_point);
677
678 let mut circuit: PlonkCircuit<Fq> = PlonkCircuit::new_turbo_plonk();
679 let point_var = circuit.create_point_variable(base_point).unwrap();
680 let endo_var = endomorphism_circuit::<_, EdwardsConfig>(&mut circuit, &point_var).unwrap();
681 let endo_point_rec = circuit.point_witness(&endo_var).unwrap();
682 assert_eq!(endo_point_rec, endo_point);
683 }
684
685 #[test]
686 fn test_decomposition() {
687 let mut rng = test_rng();
688 let lambda: Fr = Fr::from_le_bytes_mod_order(LAMBDA.as_ref());
689
690 for _ in 0..100 {
691 let scalar = Fr::rand(&mut rng);
692 let (k1, k2, is_k2_pos) = scalar_decomposition(&scalar);
693 assert!(get_bits(&k1.into_bigint().to_bits_le()) <= 128);
694 assert!(get_bits(&k2.into_bigint().to_bits_le()) <= 128);
695 let k2 = if is_k2_pos { k2 } else { -k2 };
696
697 assert_eq!(k1 - k2 * lambda, scalar,);
698
699 let mut circuit: PlonkCircuit<Fq> = PlonkCircuit::new_ultra_plonk(16);
700 let scalar_var = circuit.create_variable(field_switching(&scalar)).unwrap();
701 let (k1_var, k2_var, k2_sign_var) = scalar_decomposition_gate::<
702 <EdwardsConfig as CurveConfig>::BaseField,
703 <EdwardsConfig as CurveConfig>::ScalarField,
704 >(&mut circuit, &scalar_var)
705 .unwrap();
706
707 let k1_rec = circuit.witness(k1_var).unwrap();
708 assert_eq!(field_switching::<_, Fq>(&k1), k1_rec);
709
710 let k2_rec = circuit.witness(k2_var).unwrap();
711 let k2_sign = circuit.witness(k2_sign_var.into()).unwrap();
712 let k2_with_sign_rec = if k2_sign == Fq::one() {
713 field_switching::<_, Fr>(&k2_rec)
714 } else {
715 -field_switching::<_, Fr>(&k2_rec)
716 };
717
718 assert_eq!(k2, k2_with_sign_rec);
719 }
720 }
721}