diff --git a/crates/bls12381/Cargo.toml b/crates/bls12381/Cargo.toml index 3f3242f..3bda525 100644 --- a/crates/bls12381/Cargo.toml +++ b/crates/bls12381/Cargo.toml @@ -22,5 +22,6 @@ rand = { workspace = true} bls12_381 = { git = "https://github.com/lurk-lab/bls12_381", features = ["experimental"] } [dev-dependencies] +bellpepper = { workspace = true } expect-test = "1.4.1" halo2curves = "0.6.1" diff --git a/crates/bls12381/src/curves/g1.rs b/crates/bls12381/src/curves/g1.rs index d20c88a..8744d12 100644 --- a/crates/bls12381/src/curves/g1.rs +++ b/crates/bls12381/src/curves/g1.rs @@ -27,20 +27,22 @@ where } } -impl From<&G1Point> for G1Affine +impl TryFrom<&G1Point> for G1Affine where F: PrimeFieldBits, { - fn from(value: &G1Point) -> Self { - let x = BlsFp::from(&value.x); - let y = BlsFp::from(&value.x); + type Error = SynthesisError; + + fn try_from(value: &G1Point) -> Result { + let x = BlsFp::try_from(&value.x)?; + let y = BlsFp::try_from(&value.y)?; let z = if x.is_zero().into() && y.is_zero().into() { BlsFp::zero() } else { BlsFp::one() }; let proj = G1Projective { x, y, z }; - Self::from(proj) + Ok(Self::from(proj)) } } @@ -53,14 +55,18 @@ impl G1Point { } } - pub fn alloc_element(cs: &mut CS, value: &G1Affine) -> Result + pub fn alloc_element(cs: &mut CS, value: &Option) -> Result where CS: ConstraintSystem, { - let (x, y) = if value.is_identity().into() { - (BlsFp::zero(), BlsFp::zero()) + let (x, y) = if let Some(value) = value { + if value.is_identity().into() { + (Some(BlsFp::zero()), Some(BlsFp::zero())) + } else { + (Some(value.x), Some(value.y)) + } } else { - (value.x, value.y) + (None, None) }; let x = FpElement::::alloc_element(&mut cs.namespace(|| "allocate x (g1)"), &x)?; let y = FpElement::::alloc_element(&mut cs.namespace(|| "allocate y (g1)"), &y)?; @@ -573,9 +579,9 @@ mod tests { let c = G1Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.add(&mut cs.namespace(|| "a+b"), &b_alloc).unwrap(); G1Point::assert_is_equal(&mut cs.namespace(|| "a+b = c"), &res_alloc, &c_alloc).unwrap(); if !cs.is_satisfied() { @@ -600,12 +606,13 @@ mod tests { let d = G1Affine::from(d); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); - let d_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc d"), &d).unwrap(); + let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); + let d_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc d"), &Some(d)).unwrap(); let z_alloc = - G1Point::alloc_element(&mut cs.namespace(|| "alloc z"), &G1Affine::identity()).unwrap(); + G1Point::alloc_element(&mut cs.namespace(|| "alloc z"), &Some(G1Affine::identity())) + .unwrap(); let res1_alloc = a_alloc .add_unified(&mut cs.namespace(|| "a+b"), &b_alloc) .unwrap(); @@ -642,12 +649,13 @@ mod tests { let c = G1Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); // alloc it so it's not a constant let z_alloc = - G1Point::alloc_element(&mut cs.namespace(|| "alloc 0"), &G1Affine::identity()).unwrap(); + G1Point::alloc_element(&mut cs.namespace(|| "alloc 0"), &Some(G1Affine::identity())) + .unwrap(); G1Point::assert_addition( &mut cs.namespace(|| "assert_addition(a, b, c)"), &a_alloc, @@ -697,8 +705,8 @@ mod tests { let c = G1Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.neg(&mut cs.namespace(|| "a.neg()")).unwrap(); G1Point::assert_is_equal(&mut cs.namespace(|| "a.neg() = c"), &res_alloc, &c_alloc) .unwrap(); @@ -720,8 +728,8 @@ mod tests { let c = G1Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.triple(&mut cs.namespace(|| "a.triple()")).unwrap(); G1Point::assert_is_equal(&mut cs.namespace(|| "a.triple() = c"), &res_alloc, &c_alloc) .unwrap(); @@ -743,8 +751,8 @@ mod tests { let c = G1Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.double(&mut cs.namespace(|| "a.double()")).unwrap(); G1Point::assert_is_equal(&mut cs.namespace(|| "a.double() = c"), &res_alloc, &c_alloc) .unwrap(); @@ -768,9 +776,9 @@ mod tests { let c = G1Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.sub(&mut cs.namespace(|| "a-b"), &b_alloc).unwrap(); G1Point::assert_is_equal(&mut cs.namespace(|| "a-b = c"), &res_alloc, &c_alloc).unwrap(); if !cs.is_satisfied() { @@ -793,9 +801,9 @@ mod tests { let c = G1Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .double_and_add(&mut cs.namespace(|| "a.double_and_add(b)"), &b_alloc) .unwrap(); @@ -821,8 +829,8 @@ mod tests { let b = G1Affine::from(G1Projective::random(&mut rng)); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); + let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); let neg_a = a_alloc.neg(&mut cs.namespace(|| "-a")).unwrap(); let res_alloc = a_alloc .add_unified(&mut cs.namespace(|| "a-a"), &neg_a) @@ -868,8 +876,8 @@ mod tests { let c = G1Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .scalar_mul_by_seed_square(&mut cs.namespace(|| "a.mul_by_seed_square()")) .unwrap(); @@ -895,7 +903,7 @@ mod tests { let a = G1Affine::from(G1Projective::generator() * n); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); + let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); a_alloc .assert_subgroup_check(&mut cs.namespace(|| "a.subgroup_check()")) .unwrap(); @@ -911,7 +919,7 @@ mod tests { #[test] fn test_random_subgroup_check_negative() { use crate::curves::params::EmulatedCurveParams; - let b = BlsFp::from(&Bls12381G1Params::::b()); + let b = BlsFp::try_from(&Bls12381G1Params::::b()).unwrap(); use rand::RngCore; let mut rng = rand::thread_rng(); let mut random_point = || loop { @@ -932,7 +940,7 @@ mod tests { } let mut cs = TestConstraintSystem::::new(); - let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); + let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); a_alloc .assert_subgroup_check(&mut cs.namespace(|| "a.subgroup_check()")) .unwrap(); @@ -949,8 +957,8 @@ mod tests { let c = bls12_381::g1::endomorphism(&a); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.phi(&mut cs.namespace(|| "a.phi()")).unwrap(); G1Point::assert_is_equal(&mut cs.namespace(|| "a.phi() = c"), &res_alloc, &c_alloc) .unwrap(); diff --git a/crates/bls12381/src/curves/g2.rs b/crates/bls12381/src/curves/g2.rs index c32dea0..6f1f1c3 100644 --- a/crates/bls12381/src/curves/g2.rs +++ b/crates/bls12381/src/curves/g2.rs @@ -35,31 +35,35 @@ where } } -impl From<&G2Point> for G2Affine +impl TryFrom<&G2Point> for G2Affine where F: PrimeFieldBits, { - fn from(value: &G2Point) -> Self { - let x = BlsFp2::from(&value.x); - let y = BlsFp2::from(&value.x); + type Error = SynthesisError; + + fn try_from(value: &G2Point) -> Result { + let x = BlsFp2::try_from(&value.x)?; + let y = BlsFp2::try_from(&value.y)?; let z = if x.is_zero().into() && y.is_zero().into() { BlsFp2::zero() } else { BlsFp2::one() }; let proj = G2Projective { x, y, z }; - Self::from(proj) + Ok(Self::from(proj)) } } impl G2Point { - pub fn alloc_element(cs: &mut CS, value: &G2Affine) -> Result + pub fn alloc_element(cs: &mut CS, value: &Option) -> Result where CS: ConstraintSystem, { + let vx = value.map(|v| v.x); + let vy = value.map(|v| v.y); // (0,0) is the point at infinity - let x = Fp2Element::::alloc_element(&mut cs.namespace(|| "allocate x (g2)"), &value.x)?; - let y = Fp2Element::::alloc_element(&mut cs.namespace(|| "allocate y (g2)"), &value.y)?; + let x = Fp2Element::::alloc_element(&mut cs.namespace(|| "allocate x (g2)"), &vx)?; + let y = Fp2Element::::alloc_element(&mut cs.namespace(|| "allocate y (g2)"), &vy)?; Ok(Self { x, y }) } @@ -519,8 +523,8 @@ impl G2Point { assert_eq!(&c1 % 16, BigInt::zero(), "p^2 + 7 divisible by 16"); - let gx0_n = BlsFp2::from(&gx0); - let gx1_n = BlsFp2::from(&gx1); + let gx0_n = BlsFp2::try_from(&gx0)?; + let gx1_n = BlsFp2::try_from(&gx1)?; let sqrt_candidate0 = fp2_pow_vartime(&gx0_n, &c2); // -1 is a square in Fp2 (because p^2 - 1 is even) so we only need to check half of the 8th roots of unity @@ -550,7 +554,7 @@ impl G2Point { // square root of gX1 must be = sqrt_candidate * t^3 * eta // for one of four precomputed values of eta // eta determined by eta^2 = xi^3 * (-1)^{-1/4} - let t_native = BlsFp2::from(t); + let t_native = BlsFp2::try_from(t)?; let t3 = t_native * t_native * t_native; let sqrt_candidate1 = sqrt_candidate0 * t3; @@ -598,8 +602,10 @@ impl G2Point { if y_sgn0 != t_sgn0 { outy_val = outy_val.neg(); } - let outy = - Fp2Element::alloc_element(&mut cs.namespace(|| "alloc outy <- outy_val"), &outy_val)?; + let outy = Fp2Element::alloc_element( + &mut cs.namespace(|| "alloc outy <- outy_val"), + &Some(outy_val), + )?; // enforce that Y^2 = g(X) let y_sq = outy.square(&mut cs.namespace(|| "y_sq <- outy.square()"))?; @@ -837,9 +843,9 @@ mod tests { let c = G2Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.add(&mut cs.namespace(|| "a+b"), &b_alloc).unwrap(); G2Point::assert_is_equal(&mut cs.namespace(|| "a+b = c"), &res_alloc, &c_alloc).unwrap(); if !cs.is_satisfied() { @@ -860,8 +866,8 @@ mod tests { let c = G2Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.neg(&mut cs.namespace(|| "a.neg()")).unwrap(); G2Point::assert_is_equal(&mut cs.namespace(|| "a.neg() = c"), &res_alloc, &c_alloc) .unwrap(); @@ -883,8 +889,8 @@ mod tests { let c = G2Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.triple(&mut cs.namespace(|| "a.triple()")).unwrap(); G2Point::assert_is_equal(&mut cs.namespace(|| "a.triple() = c"), &res_alloc, &c_alloc) .unwrap(); @@ -906,8 +912,8 @@ mod tests { let c = G2Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.double(&mut cs.namespace(|| "a.double()")).unwrap(); G2Point::assert_is_equal(&mut cs.namespace(|| "a.double() = c"), &res_alloc, &c_alloc) .unwrap(); @@ -931,9 +937,9 @@ mod tests { let c = G2Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.sub(&mut cs.namespace(|| "a-b"), &b_alloc).unwrap(); G2Point::assert_is_equal(&mut cs.namespace(|| "a-b = c"), &res_alloc, &c_alloc).unwrap(); if !cs.is_satisfied() { @@ -956,9 +962,9 @@ mod tests { let c = G2Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .double_and_add(&mut cs.namespace(|| "a.double_and_add(b)"), &b_alloc) .unwrap(); @@ -988,8 +994,8 @@ mod tests { let c = G2Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .scalar_mul_by_seed(&mut cs.namespace(|| "a.mul_by_seed()")) .unwrap(); @@ -1015,7 +1021,7 @@ mod tests { let a = G2Affine::from(G2Projective::generator() * n); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); + let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); a_alloc .assert_subgroup_check(&mut cs.namespace(|| "a.subgroup_check()")) .unwrap(); @@ -1037,8 +1043,8 @@ mod tests { let c = G2Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.psi2(&mut cs.namespace(|| "a.psi2()")).unwrap(); G2Point::assert_is_equal(&mut cs.namespace(|| "a.psi2() = c"), &res_alloc, &c_alloc) .unwrap(); @@ -1060,8 +1066,8 @@ mod tests { let c = G2Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.psi(&mut cs.namespace(|| "a.psi()")).unwrap(); G2Point::assert_is_equal(&mut cs.namespace(|| "a.psi() = c"), &res_alloc, &c_alloc) .unwrap(); @@ -1083,8 +1089,8 @@ mod tests { let c = G2Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .clear_cofactor(&mut cs.namespace(|| "a.clear_cofactor()")) .unwrap(); @@ -1106,7 +1112,7 @@ mod tests { #[test] fn test_random_clear_cofactor_torsion_point() { use crate::curves::params::EmulatedCurveParams; - let b = BlsFp2::from(&Bls12381G2Params::::b()); + let b = BlsFp2::try_from(&Bls12381G2Params::::b()).unwrap(); use rand::RngCore; let mut rng = rand::thread_rng(); let mut random_point = || loop { @@ -1129,8 +1135,8 @@ mod tests { let c = G2Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .clear_cofactor(&mut cs.namespace(|| "a.clear_cofactor()")) .unwrap(); @@ -1152,7 +1158,7 @@ mod tests { #[test] fn test_random_subgroup_check_negative() { use crate::curves::params::EmulatedCurveParams; - let b = BlsFp2::from(&Bls12381G2Params::::b()); + let b = BlsFp2::try_from(&Bls12381G2Params::::b()).unwrap(); use rand::RngCore; let mut rng = rand::thread_rng(); let mut random_point = || loop { @@ -1173,7 +1179,7 @@ mod tests { } let mut cs = TestConstraintSystem::::new(); - let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); + let a_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); a_alloc .assert_subgroup_check(&mut cs.namespace(|| "a.subgroup_check()")) .unwrap(); @@ -1191,8 +1197,8 @@ mod tests { let c = G2Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = G2Point::opt_simple_swu2(&mut cs.namespace(|| "opt_simple_swu2(a)"), &a_alloc).unwrap(); G2Point::assert_is_equal( @@ -1221,8 +1227,8 @@ mod tests { let mut cs = TestConstraintSystem::::new(); let a_alloc = - G2IsoPoint(G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap()); - let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + G2IsoPoint(G2Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap()); + let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = G2Point::iso3_map(&mut cs.namespace(|| "iso3_map(a)"), &a_alloc).unwrap(); G2Point::assert_is_equal( &mut cs.namespace(|| "iso3_map(a) = c"), @@ -1250,9 +1256,9 @@ mod tests { let c = G2Affine::from(c); let mut cs = TestConstraintSystem::::new(); - let x_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc x"), &x).unwrap(); - let y_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc y"), &y).unwrap(); - let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let x_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc x"), &Some(x)).unwrap(); + let y_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc y"), &Some(y)).unwrap(); + let c_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = G2Point::map_to_g2(&mut cs.namespace(|| "map_to_g2(x, y)"), &x_alloc, &y_alloc) .unwrap(); diff --git a/crates/bls12381/src/curves/pairing.rs b/crates/bls12381/src/curves/pairing.rs index c88eda3..4f402f6 100644 --- a/crates/bls12381/src/curves/pairing.rs +++ b/crates/bls12381/src/curves/pairing.rs @@ -603,6 +603,7 @@ where #[cfg(test)] mod tests { use super::*; + use bellpepper::util_cs::metric_cs::MetricCS; use bellpepper_core::test_cs::TestConstraintSystem; use halo2curves::bn256::Fq as Fp; use halo2curves::group::Group; @@ -614,6 +615,24 @@ mod tests { expected.assert_eq(&computed.to_string()); } + #[test] + fn test_pairing_metric_cs() { + let mut cs = MetricCS::::new(); + let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &None).unwrap(); + let b_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc b"), &None).unwrap(); + let c_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &None).unwrap(); + let res_alloc = EmulatedBls12381Pairing::pair( + &mut cs.namespace(|| "pair(a, b)"), + &[a_alloc], + &[b_alloc], + ) + .unwrap(); + Fp12Element::assert_is_equal(&mut cs.namespace(|| "pair(a, b) = c"), &res_alloc, &c_alloc) + .unwrap(); + expect_eq(cs.num_inputs(), &expect!["1"]); + expect_eq(cs.num_constraints(), &expect!["7147240"]); + } + // NOTE: this test currently takes ~22GB of ram and ~50s to run #[test] fn test_random_pairing() { @@ -626,9 +645,10 @@ mod tests { let c = c.0; let mut cs = TestConstraintSystem::::new(); - let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = G1Point::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = G2Point::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = EmulatedBls12381Pairing::pair( &mut cs.namespace(|| "pair(a, b)"), &[a_alloc], @@ -671,17 +691,20 @@ mod tests { .iter() .enumerate() .map(|(idx, a)| { - G1Point::alloc_element(&mut cs.namespace(|| format!("alloc a {idx}")), a).unwrap() + G1Point::alloc_element(&mut cs.namespace(|| format!("alloc a {idx}")), &Some(*a)) + .unwrap() }) .collect(); let b_allocs: Vec> = b .iter() .enumerate() .map(|(idx, b)| { - G2Point::alloc_element(&mut cs.namespace(|| format!("alloc b {idx}")), b).unwrap() + G2Point::alloc_element(&mut cs.namespace(|| format!("alloc b {idx}")), &Some(*b)) + .unwrap() }) .collect(); - let c_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let c_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = EmulatedBls12381Pairing::pair(&mut cs.namespace(|| "pair(a, b)"), &a_allocs, &b_allocs) .unwrap(); diff --git a/crates/bls12381/src/fields/fp.rs b/crates/bls12381/src/fields/fp.rs index df94483..41e44d5 100644 --- a/crates/bls12381/src/fields/fp.rs +++ b/crates/bls12381/src/fields/fp.rs @@ -110,14 +110,14 @@ pub(crate) fn bigint_to_fpelem(val: &BigInt) -> Option { Some(BlsFp::from_bytes(&bytes).unwrap()) } -pub(crate) fn emulated_to_native(value: &Bls12381Fp) -> BlsFp +pub(crate) fn emulated_to_native(value: &Bls12381Fp) -> Option where F: PrimeFieldBits, { use std::ops::Rem; let p = &Bls12381FpParams::modulus(); - let val = BigInt::from(value).rem(p); - bigint_to_fpelem(&val).unwrap() + let val = BigInt::try_from(value).ok().map(|v| v.rem(p)); + val.and_then(|v| bigint_to_fpelem(&v)) } pub(crate) fn big_from_dec(v: &str) -> Option { @@ -128,20 +128,24 @@ pub(crate) fn fp_from_dec(v: &str) -> Option { big_from_dec(v).as_ref().and_then(bigint_to_fpelem) } -impl From<&FpElement> for BlsFp +impl TryFrom<&FpElement> for BlsFp where F: PrimeFieldBits, { - fn from(value: &FpElement) -> Self { - emulated_to_native(&value.0) + type Error = SynthesisError; + + fn try_from(value: &FpElement) -> Result { + emulated_to_native(&value.0).ok_or(SynthesisError::AssignmentMissing) } } -impl From<&FpElement> for BigInt { - fn from(value: &FpElement) -> Self { +impl TryFrom<&FpElement> for BigInt { + type Error = SynthesisError; + + fn try_from(value: &FpElement) -> Result { use std::ops::Rem; let p = &Bls12381FpParams::modulus(); - Self::from(&value.0).rem(p) + Self::try_from(&value.0).map(|v| v.rem(p)) } } @@ -158,14 +162,15 @@ impl FpElement { Self(Bls12381Fp::one()) } - pub fn alloc_element(cs: &mut CS, value: &BlsFp) -> Result + pub fn alloc_element(cs: &mut CS, value: &Option) -> Result where CS: ConstraintSystem, { - let val_alloc = Self::from(value); - let alloc = val_alloc - .0 - .allocate_field_element_unchecked(&mut cs.namespace(|| "alloc fp elm"))?; + let val: Option = value.and_then(|v| BigInt::try_from(&Self::from(&v)).ok()); + let alloc = Bls12381Fp::::allocate_optional_field_element_unchecked( + &mut cs.namespace(|| "alloc fp elm"), + &val, + )?; Ok(Self(alloc)) } @@ -324,9 +329,9 @@ mod tests { let c = a + b; let mut cs = TestConstraintSystem::::new(); - let a_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.add(&mut cs.namespace(|| "a+b"), &b_alloc).unwrap(); FpElement::assert_is_equal(&mut cs.namespace(|| "a+b = c"), &res_alloc, &c_alloc).unwrap(); if !cs.is_satisfied() { @@ -346,9 +351,9 @@ mod tests { let c = a - b; let mut cs = TestConstraintSystem::::new(); - let a_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.sub(&mut cs.namespace(|| "a-b"), &b_alloc).unwrap(); FpElement::assert_is_equal(&mut cs.namespace(|| "a-b = c"), &res_alloc, &c_alloc).unwrap(); if !cs.is_satisfied() { @@ -368,9 +373,9 @@ mod tests { let c = a * b; let mut cs = TestConstraintSystem::::new(); - let a_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.mul(&mut cs.namespace(|| "a*b"), &b_alloc).unwrap(); FpElement::assert_is_equal(&mut cs.namespace(|| "a*b = c"), &res_alloc, &c_alloc).unwrap(); assert!(cs.is_satisfied()); @@ -395,10 +400,10 @@ mod tests { let c = a * b; let mut cs = TestConstraintSystem::::new(); - let a_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); + let a_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); let b_elem: FpElement = (&b).into(); - let b_val: BigInt = (&b_elem.0).into(); - let c_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let b_val: BigInt = (&b_elem.0).try_into().unwrap(); + let c_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .mul_const(&mut cs.namespace(|| "a*b (const)"), &b_val) .unwrap(); @@ -419,8 +424,8 @@ mod tests { let c = -&a; let mut cs = TestConstraintSystem::::new(); - let a_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.neg(&mut cs.namespace(|| "-a")).unwrap(); FpElement::assert_is_equal(&mut cs.namespace(|| "-a = c"), &res_alloc, &c_alloc).unwrap(); if !cs.is_satisfied() { @@ -439,7 +444,7 @@ mod tests { let c: bool = a.sgn0().into(); let mut cs = TestConstraintSystem::::new(); - let a_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); + let a_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); let c_alloc = AllocatedBit::alloc(&mut cs.namespace(|| "alloc c"), Some(c)).unwrap(); let res_alloc = a_alloc.sgn0(&mut cs.namespace(|| "a.sgn0()")).unwrap(); Boolean::enforce_equal( @@ -464,8 +469,8 @@ mod tests { let b = BlsFp::random(&mut rng); let mut cs = TestConstraintSystem::::new(); - let a_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); + let a_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = FpElement::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); let res_alloc = a_alloc.sub(&mut cs.namespace(|| "a-a"), &a_alloc).unwrap(); let zero = FpElement::zero(); FpElement::assert_is_equal(&mut cs.namespace(|| "a-a = 0"), &res_alloc, &zero).unwrap(); diff --git a/crates/bls12381/src/fields/fp12.rs b/crates/bls12381/src/fields/fp12.rs index ee409b5..edf2302 100644 --- a/crates/bls12381/src/fields/fp12.rs +++ b/crates/bls12381/src/fields/fp12.rs @@ -24,14 +24,16 @@ where } } -impl From<&Fp12Element> for BlsFp12 +impl TryFrom<&Fp12Element> for BlsFp12 where F: PrimeFieldBits, { - fn from(value: &Fp12Element) -> Self { - let c0 = BlsFp6::from(&value.c0); - let c1 = BlsFp6::from(&value.c1); - Self { c0, c1 } + type Error = SynthesisError; + + fn try_from(value: &Fp12Element) -> Result { + let c0 = BlsFp6::try_from(&value.c0)?; + let c1 = BlsFp6::try_from(&value.c1)?; + Ok(Self { c0, c1 }) } } @@ -50,12 +52,14 @@ impl Fp12Element { } } - pub fn alloc_element(cs: &mut CS, value: &BlsFp12) -> Result + pub fn alloc_element(cs: &mut CS, value: &Option) -> Result where CS: ConstraintSystem, { - let c0 = Fp6Element::::alloc_element(&mut cs.namespace(|| "allocate c0"), &value.c0)?; - let c1 = Fp6Element::::alloc_element(&mut cs.namespace(|| "allocate c1"), &value.c1)?; + let vc0 = value.map(|v| v.c0); + let vc1 = value.map(|v| v.c1); + let c0 = Fp6Element::::alloc_element(&mut cs.namespace(|| "allocate c0"), &vc0)?; + let c1 = Fp6Element::::alloc_element(&mut cs.namespace(|| "allocate c1"), &vc1)?; Ok(Self { c0, c1 }) } @@ -335,12 +339,14 @@ impl Fp12Element { where CS: ConstraintSystem, { - let val = BlsFp12::from(self); - if val.is_zero().into() { - eprintln!("Inverse of zero element cannot be calculated"); - return Err(SynthesisError::DivisionByZero); - } - let inv = val.invert().unwrap(); + let val = BlsFp12::try_from(self).ok(); + let inv = val.and_then(|val| { + if val.is_zero().into() { + eprintln!("Inverse of zero Fp12 element cannot be calculated"); + return None; + } + Some(val.invert().unwrap()) + }); let inv_alloc = Self::alloc_element(&mut cs.namespace(|| "alloc inv"), &inv)?; @@ -358,15 +364,18 @@ impl Fp12Element { { // returns self/value (or x/y) - let x = BlsFp12::from(self); - - let y = BlsFp12::from(value); - if y.is_zero().into() { - eprintln!("Inverse of zero element cannot be calculated"); - return Err(SynthesisError::DivisionByZero); - } - let y_inv = y.invert().unwrap(); - let div = y_inv * x; + let x = BlsFp12::try_from(self).ok(); + let y = BlsFp12::try_from(value).ok(); + let div = x.and_then(|x| { + y.and_then(|y| { + if y.is_zero().into() { + eprintln!("Division by zero Fp12 element cannot be calculated"); + return None; + } + let y_inv = y.invert().unwrap(); + Some(y_inv * x) + }) + }); let div_alloc = Self::alloc_element(&mut cs.namespace(|| "alloc div"), &div)?; @@ -421,9 +430,12 @@ mod tests { let c = a + b; let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.add(&mut cs.namespace(|| "a+b"), &b_alloc).unwrap(); Fp12Element::assert_is_equal(&mut cs.namespace(|| "a+b = c"), &res_alloc, &c_alloc) .unwrap(); @@ -444,9 +456,12 @@ mod tests { let c = a - b; let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.sub(&mut cs.namespace(|| "a-b"), &b_alloc).unwrap(); Fp12Element::assert_is_equal(&mut cs.namespace(|| "a-b = c"), &res_alloc, &c_alloc) .unwrap(); @@ -469,9 +484,12 @@ mod tests { let c = a.mul(b); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.mul(&mut cs.namespace(|| "a*b"), &b_alloc).unwrap(); Fp12Element::assert_is_equal(&mut cs.namespace(|| "a*b = c"), &res_alloc, &c_alloc) .unwrap(); @@ -491,8 +509,10 @@ mod tests { let c = a.square(); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.square(&mut cs.namespace(|| "a²")).unwrap(); Fp12Element::assert_is_equal(&mut cs.namespace(|| "a² = c"), &res_alloc, &c_alloc).unwrap(); if !cs.is_satisfied() { @@ -515,9 +535,12 @@ mod tests { let c = a * b.invert().unwrap(); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .div_unchecked(&mut cs.namespace(|| "a div b"), &b_alloc) .unwrap(); @@ -555,10 +578,14 @@ mod tests { let c = a * b; let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c0_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c0"), &c0).unwrap(); - let c1_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c1"), &c1).unwrap(); - let c_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c0_alloc = + Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c0"), &Some(c0)).unwrap(); + let c1_alloc = + Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c1"), &Some(c1)).unwrap(); + let c_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .mul_by_014( &mut cs.namespace(|| "a*(c0, c1)(014)"), @@ -613,11 +640,16 @@ mod tests { let c = a * b; let mut cs = TestConstraintSystem::::new(); - let c0_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c0"), &c0).unwrap(); - let c1_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c1"), &c1).unwrap(); - let d0_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc d0"), &d0).unwrap(); - let d1_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc d1"), &d1).unwrap(); - let c_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let c0_alloc = + Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c0"), &Some(c0)).unwrap(); + let c1_alloc = + Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c1"), &Some(c1)).unwrap(); + let d0_alloc = + Fp2Element::alloc_element(&mut cs.namespace(|| "alloc d0"), &Some(d0)).unwrap(); + let d1_alloc = + Fp2Element::alloc_element(&mut cs.namespace(|| "alloc d1"), &Some(d1)).unwrap(); + let c_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = Fp12Element::mul_014_by_014( &mut cs.namespace(|| "(c0, c1)(014)*(d0, d1)(014)"), &c0_alloc, @@ -663,13 +695,20 @@ mod tests { let c = a * b; let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c00_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c00"), &c00).unwrap(); - let c01_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c01"), &c01).unwrap(); - let c02_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c02"), &c02).unwrap(); - let c11_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c11"), &c11).unwrap(); - let c12_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c12"), &c12).unwrap(); - let c_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c00_alloc = + Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c00"), &Some(c00)).unwrap(); + let c01_alloc = + Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c01"), &Some(c01)).unwrap(); + let c02_alloc = + Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c02"), &Some(c02)).unwrap(); + let c11_alloc = + Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c11"), &Some(c11)).unwrap(); + let c12_alloc = + Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c12"), &Some(c12)).unwrap(); + let c_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .mul_by_01245( &mut cs.namespace(|| "a*(c00, c01, c01, c11, c12)(01245)"), @@ -698,8 +737,10 @@ mod tests { let c = a.conjugate(); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .conjugate(&mut cs.namespace(|| "a.conjugate()")) .unwrap(); @@ -722,11 +763,13 @@ mod tests { fn test_random_inverse() { let mut rng = rand::thread_rng(); let a = BlsFp12::random(&mut rng); - let c = a.invert().unwrap_or_else(BlsFp12::zero); + let c = a.invert().unwrap(); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.inverse(&mut cs.namespace(|| "a^-1")).unwrap(); Fp12Element::assert_is_equal(&mut cs.namespace(|| "a^-1 = c"), &res_alloc, &c_alloc) .unwrap(); @@ -746,8 +789,10 @@ mod tests { let b = BlsFp12::random(&mut rng); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); + let a_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); let res_alloc = a_alloc.sub(&mut cs.namespace(|| "a-a"), &a_alloc).unwrap(); let zero = Fp12Element::zero(); Fp12Element::assert_is_equal(&mut cs.namespace(|| "a-a = 0"), &res_alloc, &zero).unwrap(); diff --git a/crates/bls12381/src/fields/fp2.rs b/crates/bls12381/src/fields/fp2.rs index 9f7bf51..442efbb 100644 --- a/crates/bls12381/src/fields/fp2.rs +++ b/crates/bls12381/src/fields/fp2.rs @@ -25,14 +25,16 @@ where } } -impl From<&Fp2Element> for BlsFp2 +impl TryFrom<&Fp2Element> for BlsFp2 where F: PrimeFieldBits, { - fn from(value: &Fp2Element) -> Self { - let c0 = BlsFp::from(&value.a0); - let c1 = BlsFp::from(&value.a1); - Self { c0, c1 } + type Error = SynthesisError; + + fn try_from(value: &Fp2Element) -> Result { + let c0 = BlsFp::try_from(&value.a0)?; + let c1 = BlsFp::try_from(&value.a1)?; + Ok(Self { c0, c1 }) } } @@ -94,12 +96,14 @@ impl Fp2Element { } } - pub fn alloc_element(cs: &mut CS, value: &BlsFp2) -> Result + pub fn alloc_element(cs: &mut CS, value: &Option) -> Result where CS: ConstraintSystem, { - let a0 = FpElement::::alloc_element(&mut cs.namespace(|| "allocate a0"), &value.c0)?; - let a1 = FpElement::::alloc_element(&mut cs.namespace(|| "allocate a1"), &value.c1)?; + let c0 = value.map(|v| v.c0); + let c1 = value.map(|v| v.c1); + let a0 = FpElement::::alloc_element(&mut cs.namespace(|| "allocate a0"), &c0)?; + let a1 = FpElement::::alloc_element(&mut cs.namespace(|| "allocate a1"), &c1)?; Ok(Self { a0, a1 }) } @@ -403,12 +407,14 @@ impl Fp2Element { where CS: ConstraintSystem, { - let val = BlsFp2::from(self); - if val.is_zero().into() { - eprintln!("Inverse of zero element cannot be calculated"); - return Err(SynthesisError::DivisionByZero); - } - let inv = val.invert().unwrap(); + let val = BlsFp2::try_from(self).ok(); + let inv = val.and_then(|val| { + if val.is_zero().into() { + eprintln!("Inverse of zero Fp2 element cannot be calculated"); + return None; + } + Some(val.invert().unwrap()) + }); let inv_alloc = Self::alloc_element(&mut cs.namespace(|| "alloc inv"), &inv)?; @@ -426,15 +432,18 @@ impl Fp2Element { { // returns self/value (or x/y) - let x = BlsFp2::from(self); - - let y = BlsFp2::from(value); - if y.is_zero().into() { - eprintln!("Inverse of zero element cannot be calculated"); - return Err(SynthesisError::DivisionByZero); - } - let y_inv = y.invert().unwrap(); - let div = y_inv.mul(&x); + let x = BlsFp2::try_from(self).ok(); + let y = BlsFp2::try_from(value).ok(); + let div = x.and_then(|x| { + y.and_then(|y| { + if y.is_zero().into() { + eprintln!("Division by zero Fp2 element cannot be calculated"); + return None; + } + let y_inv = y.invert().unwrap(); + Some(y_inv.mul(&x)) + }) + }); let div_alloc = Self::alloc_element(&mut cs.namespace(|| "alloc div"), &div)?; @@ -512,9 +521,9 @@ mod tests { let c = a + b; let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.add(&mut cs.namespace(|| "a+b"), &b_alloc).unwrap(); Fp2Element::assert_is_equal(&mut cs.namespace(|| "a+b = c"), &res_alloc, &c_alloc).unwrap(); if !cs.is_satisfied() { @@ -534,9 +543,9 @@ mod tests { let c = a - b; let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.sub(&mut cs.namespace(|| "a-b"), &b_alloc).unwrap(); Fp2Element::assert_is_equal(&mut cs.namespace(|| "a-b = c"), &res_alloc, &c_alloc).unwrap(); if !cs.is_satisfied() { @@ -555,8 +564,8 @@ mod tests { let c = a + a; let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.double(&mut cs.namespace(|| "2a")).unwrap(); Fp2Element::assert_is_equal(&mut cs.namespace(|| "2a = c"), &res_alloc, &c_alloc).unwrap(); if !cs.is_satisfied() { @@ -576,9 +585,9 @@ mod tests { let c = a * b; let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.mul(&mut cs.namespace(|| "a*b"), &b_alloc).unwrap(); Fp2Element::assert_is_equal(&mut cs.namespace(|| "a*b = c"), &res_alloc, &c_alloc).unwrap(); if !cs.is_satisfied() { @@ -597,8 +606,8 @@ mod tests { let c = a.mul_by_nonresidue(); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .mul_by_nonresidue(&mut cs.namespace(|| "a*(1+u)")) .unwrap(); @@ -620,8 +629,8 @@ mod tests { let c = a * a; let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.square(&mut cs.namespace(|| "a²")).unwrap(); Fp2Element::assert_is_equal(&mut cs.namespace(|| "a² = c"), &res_alloc, &c_alloc).unwrap(); if !cs.is_satisfied() { @@ -644,9 +653,9 @@ mod tests { let c = a * b.invert().unwrap(); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .div_unchecked(&mut cs.namespace(|| "a div b"), &b_alloc) .unwrap(); @@ -669,9 +678,9 @@ mod tests { let c = a * BlsFp2::from(b); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); + let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); let b_elem: FpElement = (&b).into(); - let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .mul_element(&mut cs.namespace(|| "a*b (elm)"), &b_elem) .unwrap(); @@ -698,10 +707,10 @@ mod tests { let c = a * BlsFp2::from(b); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); + let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); let b_elem: FpElement = (&b).into(); - let b_val: BigInt = (&b_elem.0).into(); - let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let b_val: BigInt = (&b_elem.0).try_into().unwrap(); + let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .mul_const(&mut cs.namespace(|| "a*b (const)"), &b_val) .unwrap(); @@ -722,7 +731,7 @@ mod tests { let c: bool = a.sgn0().into(); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); + let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); let c_alloc = AllocatedBit::alloc(&mut cs.namespace(|| "alloc c"), Some(c)).unwrap(); let res_alloc = a_alloc.sgn0(&mut cs.namespace(|| "a.sgn0()")).unwrap(); Boolean::enforce_equal( @@ -751,7 +760,7 @@ mod tests { let c: bool = a.sgn0().into(); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); + let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); let c_alloc = AllocatedBit::alloc(&mut cs.namespace(|| "alloc c"), Some(c)).unwrap(); let res_alloc = a_alloc.sgn0(&mut cs.namespace(|| "a.sgn0()")).unwrap(); Boolean::enforce_equal( @@ -776,8 +785,8 @@ mod tests { let c = -&a; let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.neg(&mut cs.namespace(|| "-a")).unwrap(); Fp2Element::assert_is_equal(&mut cs.namespace(|| "-a = c"), &res_alloc, &c_alloc).unwrap(); if !cs.is_satisfied() { @@ -796,8 +805,8 @@ mod tests { let c = a.conjugate(); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.conjugate(&mut cs.namespace(|| "conj(a)")).unwrap(); Fp2Element::assert_is_equal(&mut cs.namespace(|| "conj(a) = c"), &res_alloc, &c_alloc) .unwrap(); @@ -814,11 +823,11 @@ mod tests { fn test_random_inverse() { let mut rng = rand::thread_rng(); let a = BlsFp2::random(&mut rng); - let c = a.invert().unwrap_or_else(BlsFp2::zero); + let c = a.invert().unwrap(); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.inverse(&mut cs.namespace(|| "a^-1")).unwrap(); Fp2Element::assert_is_equal(&mut cs.namespace(|| "a^-1 = c"), &res_alloc, &c_alloc) .unwrap(); @@ -838,8 +847,8 @@ mod tests { let b = BlsFp2::random(&mut rng); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); + let a_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); let res_alloc = a_alloc.sub(&mut cs.namespace(|| "a-a"), &a_alloc).unwrap(); let zero = Fp2Element::zero(); Fp2Element::assert_is_equal(&mut cs.namespace(|| "a-a = 0"), &res_alloc, &zero).unwrap(); diff --git a/crates/bls12381/src/fields/fp6.rs b/crates/bls12381/src/fields/fp6.rs index c6143ff..ca0ad01 100644 --- a/crates/bls12381/src/fields/fp6.rs +++ b/crates/bls12381/src/fields/fp6.rs @@ -25,15 +25,17 @@ where } } -impl From<&Fp6Element> for BlsFp6 +impl TryFrom<&Fp6Element> for BlsFp6 where F: PrimeFieldBits, { - fn from(value: &Fp6Element) -> Self { - let c0 = BlsFp2::from(&value.b0); - let c1 = BlsFp2::from(&value.b1); - let c2 = BlsFp2::from(&value.b2); - Self { c0, c1, c2 } + type Error = SynthesisError; + + fn try_from(value: &Fp6Element) -> Result { + let c0 = BlsFp2::try_from(&value.b0)?; + let c1 = BlsFp2::try_from(&value.b1)?; + let c2 = BlsFp2::try_from(&value.b2)?; + Ok(Self { c0, c1, c2 }) } } @@ -54,13 +56,16 @@ impl Fp6Element { } } - pub fn alloc_element(cs: &mut CS, value: &BlsFp6) -> Result + pub fn alloc_element(cs: &mut CS, value: &Option) -> Result where CS: ConstraintSystem, { - let b0 = Fp2Element::::alloc_element(&mut cs.namespace(|| "allocate b0"), &value.c0)?; - let b1 = Fp2Element::::alloc_element(&mut cs.namespace(|| "allocate b1"), &value.c1)?; - let b2 = Fp2Element::::alloc_element(&mut cs.namespace(|| "allocate b2"), &value.c2)?; + let c0 = value.map(|v| v.c0); + let c1 = value.map(|v| v.c1); + let c2 = value.map(|v| v.c2); + let b0 = Fp2Element::::alloc_element(&mut cs.namespace(|| "allocate b0"), &c0)?; + let b1 = Fp2Element::::alloc_element(&mut cs.namespace(|| "allocate b1"), &c1)?; + let b2 = Fp2Element::::alloc_element(&mut cs.namespace(|| "allocate b2"), &c2)?; Ok(Self { b0, b1, b2 }) } @@ -366,12 +371,14 @@ impl Fp6Element { where CS: ConstraintSystem, { - let val = BlsFp6::from(self); - if val.is_zero().into() { - eprintln!("Inverse of zero element cannot be calculated"); - return Err(SynthesisError::DivisionByZero); - } - let inv = val.invert().unwrap(); + let val = BlsFp6::try_from(self).ok(); + let inv = val.and_then(|val| { + if val.is_zero().into() { + eprintln!("Inverse of zero Fp6 element cannot be calculated"); + return None; + } + Some(val.invert().unwrap()) + }); let inv_alloc = Self::alloc_element(&mut cs.namespace(|| "alloc inv"), &inv)?; @@ -389,15 +396,18 @@ impl Fp6Element { { // returns self/value (or x/y) - let x = BlsFp6::from(self); - - let y = BlsFp6::from(value); - if y.is_zero().into() { - eprintln!("Inverse of zero element cannot be calculated"); - return Err(SynthesisError::DivisionByZero); - } - let y_inv = y.invert().unwrap(); - let div = y_inv * x; + let x = BlsFp6::try_from(self).ok(); + let y = BlsFp6::try_from(value).ok(); + let div = x.and_then(|x| { + y.and_then(|y| { + if y.is_zero().into() { + eprintln!("Division by zero Fp6 element cannot be calculated"); + return None; + } + let y_inv = y.invert().unwrap(); + Some(y_inv * x) + }) + }); let div_alloc = Self::alloc_element(&mut cs.namespace(|| "alloc div"), &div)?; @@ -458,9 +468,9 @@ mod tests { let c = a + b; let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.add(&mut cs.namespace(|| "a+b"), &b_alloc).unwrap(); Fp6Element::assert_is_equal(&mut cs.namespace(|| "a+b = c"), &res_alloc, &c_alloc).unwrap(); if !cs.is_satisfied() { @@ -480,9 +490,9 @@ mod tests { let c = a - b; let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.sub(&mut cs.namespace(|| "a-b"), &b_alloc).unwrap(); Fp6Element::assert_is_equal(&mut cs.namespace(|| "a-b = c"), &res_alloc, &c_alloc).unwrap(); if !cs.is_satisfied() { @@ -501,8 +511,8 @@ mod tests { let c = a + a; let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.double(&mut cs.namespace(|| "2a")).unwrap(); Fp6Element::assert_is_equal(&mut cs.namespace(|| "2a = c"), &res_alloc, &c_alloc).unwrap(); if !cs.is_satisfied() { @@ -524,9 +534,9 @@ mod tests { let c = a.mul(b); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.mul(&mut cs.namespace(|| "a*b"), &b_alloc).unwrap(); Fp6Element::assert_is_equal(&mut cs.namespace(|| "a*b = c"), &res_alloc, &c_alloc).unwrap(); if !cs.is_satisfied() { @@ -545,8 +555,8 @@ mod tests { let c = a.mul_by_nonresidue(); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .mul_by_nonresidue(&mut cs.namespace(|| "a*(1+u)")) .unwrap(); @@ -568,8 +578,8 @@ mod tests { let c = a.square(); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.square(&mut cs.namespace(|| "a²")).unwrap(); Fp6Element::assert_is_equal(&mut cs.namespace(|| "a² = c"), &res_alloc, &c_alloc).unwrap(); if !cs.is_satisfied() { @@ -592,9 +602,9 @@ mod tests { let c = a * b.invert().unwrap(); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .div_unchecked(&mut cs.namespace(|| "a div b"), &b_alloc) .unwrap(); @@ -617,9 +627,9 @@ mod tests { let c = a * BlsFp6::from(b); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .mul_by_e2(&mut cs.namespace(|| "a*b (e2)"), &b_alloc) .unwrap(); @@ -646,9 +656,10 @@ mod tests { let c = a * b; let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b0_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b0"), &b0).unwrap(); - let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b0_alloc = + Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b0"), &Some(b0)).unwrap(); + let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .mul_by_0(&mut cs.namespace(|| "a*b (e2)"), &b0_alloc) .unwrap(); @@ -676,10 +687,12 @@ mod tests { let c = a * b; let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b1_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b1"), &b1).unwrap(); - let b2_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b2"), &b2).unwrap(); - let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b1_alloc = + Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b1"), &Some(b1)).unwrap(); + let b2_alloc = + Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b2"), &Some(b2)).unwrap(); + let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .mul_by_12(&mut cs.namespace(|| "a*b (e2)"), &b1_alloc, &b2_alloc) .unwrap(); @@ -702,10 +715,12 @@ mod tests { let c = a.mul_by_01(&b0, &b1); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b0_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b0"), &b0).unwrap(); - let b1_alloc = Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b1"), &b1).unwrap(); - let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b0_alloc = + Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b0"), &Some(b0)).unwrap(); + let b1_alloc = + Fp2Element::alloc_element(&mut cs.namespace(|| "alloc b1"), &Some(b1)).unwrap(); + let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc .mul_by_01(&mut cs.namespace(|| "a*b (e2)"), &b0_alloc, &b1_alloc) .unwrap(); @@ -726,8 +741,8 @@ mod tests { let c = -&a; let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.neg(&mut cs.namespace(|| "-a")).unwrap(); Fp6Element::assert_is_equal(&mut cs.namespace(|| "-a = c"), &res_alloc, &c_alloc).unwrap(); if !cs.is_satisfied() { @@ -743,11 +758,11 @@ mod tests { fn test_random_inverse() { let mut rng = rand::thread_rng(); let a = BlsFp6::random(&mut rng); - let c = a.invert().unwrap_or_else(BlsFp6::zero); + let c = a.invert().unwrap(); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = a_alloc.inverse(&mut cs.namespace(|| "a^-1")).unwrap(); Fp6Element::assert_is_equal(&mut cs.namespace(|| "a^-1 = c"), &res_alloc, &c_alloc) .unwrap(); @@ -767,8 +782,8 @@ mod tests { let b = BlsFp6::random(&mut rng); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); + let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); let res_alloc = a_alloc.sub(&mut cs.namespace(|| "a-a"), &a_alloc).unwrap(); let zero = Fp6Element::zero(); Fp6Element::assert_is_equal(&mut cs.namespace(|| "a-a = 0"), &res_alloc, &zero).unwrap(); diff --git a/crates/bls12381/src/fields/torus.rs b/crates/bls12381/src/fields/torus.rs index 48cc4e9..b404adc 100644 --- a/crates/bls12381/src/fields/torus.rs +++ b/crates/bls12381/src/fields/torus.rs @@ -128,12 +128,13 @@ impl Torus { CS: ConstraintSystem, { let y = &self.0; - let val = BlsFp6::from(y); - let val = Self::decompress_native(&val)?; - let val = val.square(); // NOTE: could be cyclotomic_square, but I think that only makes it faster? - - let val = Self::compress_native(&val)?; + let val = BlsFp6::try_from(y).ok().and_then(|val| { + Self::decompress_native(&val) + .ok() + .map(|val| val.square()) // NOTE: could be cyclotomic_square, but I think that only makes it faster? Which doesn't matter in the evaluation + .and_then(|val| Self::compress_native(&val).ok()) + }); let sq_alloc = Fp6Element::::alloc_element(&mut cs.namespace(|| "alloc torus square"), &val)?; // x @@ -314,8 +315,9 @@ mod tests { let c = Torus::::compress_native(&a).unwrap(); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res_alloc = Torus::compress(&mut cs.namespace(|| "a.torus()"), &a_alloc).unwrap(); Fp6Element::assert_is_equal( &mut cs.namespace(|| "a.torus() = c"), @@ -339,8 +341,9 @@ mod tests { let c = Torus::::decompress_native(&a).unwrap(); let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let c_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let c_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let res = Torus(a_alloc); let res_alloc = res .decompress(&mut cs.namespace(|| "a.decompress()")) @@ -380,9 +383,11 @@ mod tests { }; let mut cs = TestConstraintSystem::::new(); - let a_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &a).unwrap(); - let b_alloc = Fp12Element::alloc_element(&mut cs.namespace(|| "alloc b"), &b).unwrap(); - let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &c).unwrap(); + let a_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc a"), &Some(a)).unwrap(); + let b_alloc = + Fp12Element::alloc_element(&mut cs.namespace(|| "alloc b"), &Some(b)).unwrap(); + let c_alloc = Fp6Element::alloc_element(&mut cs.namespace(|| "alloc c"), &Some(c)).unwrap(); let a_alloc = Torus::compress(&mut cs.namespace(|| "a <- a.torus()"), &a_alloc).unwrap(); let b_alloc = Torus::compress(&mut cs.namespace(|| "b <- b.torus()"), &b_alloc).unwrap(); let res_alloc = a_alloc.mul(&mut cs.namespace(|| "a*b"), &b_alloc).unwrap(); diff --git a/crates/emulated/src/field_element.rs b/crates/emulated/src/field_element.rs index 119e945..0d74e58 100644 --- a/crates/emulated/src/field_element.rs +++ b/crates/emulated/src/field_element.rs @@ -57,6 +57,27 @@ where Self::Allocated(num_vec) } + + /// Used to allocate EmulatedLimbs when no assignments are provided. Useful for MetricCS. + pub(crate) fn allocate_empty_limbs( + mut cs: CS, + limb_count: usize, + ) -> Result + where + CS: ConstraintSystem, + { + let mut num_vec: Vec> = vec![]; + + for i in 0..limb_count { + let allocated_limb = + AllocatedNum::alloc(cs.namespace(|| format!("allocating limb {i}")), || { + Err(SynthesisError::AssignmentMissing) + })?; + num_vec.push(Num::::from(allocated_limb)); + } + + Ok(Self::Allocated(num_vec)) + } } /// Parameters of a prime of the form `2^e-c` @@ -155,27 +176,29 @@ where } } -impl From<&EmulatedFieldElement> for BigInt +impl TryFrom<&EmulatedFieldElement> for BigInt where F: PrimeFieldBits, P: EmulatedFieldParams, { - fn from(value: &EmulatedFieldElement) -> Self { + type Error = SynthesisError; + + fn try_from(value: &EmulatedFieldElement) -> Result { let mut res: BigUint = Zero::zero(); let one: &BigUint = &One::one(); let mut base: BigUint = one.clone(); let limbs = match value.limbs.clone() { EmulatedLimbs::Allocated(x) => x .into_iter() - .map(|a| a.get_value().unwrap_or_default()) - .collect(), + .map(|a| a.get_value().ok_or(SynthesisError::AssignmentMissing)) + .collect::>()?, EmulatedLimbs::Constant(x) => x, }; for limb in limbs { res += base.clone() * BigUint::from_bytes_le(limb.to_repr().as_ref()); base *= one << P::bits_per_limb(); } - Self::from(res) + Ok(Self::from(res)) } } @@ -220,11 +243,30 @@ where matches!(self.limbs, EmulatedLimbs::Constant(_)) } + pub fn allocate_optional_limbs( + cs: &mut CS, + value: Option, + ) -> Result, SynthesisError> + where + CS: ConstraintSystem, + { + // This is uniform because constants generated by the `From` impl always + // have exactly `P::num_limbs()` limbs + if let Some(val) = value { + let res = Self::from(&val); + assert_eq!(res.len(), P::num_limbs()); + res.allocate_limbs(cs) + } else { + EmulatedLimbs::::allocate_empty_limbs(cs, P::num_limbs()) + } + } + pub fn allocate_limbs(&self, cs: &mut CS) -> Result, SynthesisError> where CS: ConstraintSystem, { if let EmulatedLimbs::Constant(limb_values) = &self.limbs { + assert_eq!(limb_values.len(), P::num_limbs()); Ok(EmulatedLimbs::::allocate_limbs( &mut cs.namespace(|| "allocate variables from constant limbs"), limb_values, @@ -284,6 +326,23 @@ where Ok(final_bit.unwrap()) } + pub fn allocate_optional_field_element_unchecked( + cs: &mut CS, + value: &Option, + ) -> Result + where + CS: ConstraintSystem, + { + if let Some(value) = value { + let res = Self::from(value); + res.allocate_field_element_unchecked(cs) + } else { + let allocated_limbs = Self::allocate_optional_limbs(cs, None)?; + let allocated_field_element = Self::new_internal_element(allocated_limbs, 0); + Ok(allocated_field_element) + } + } + /// Allocates an emulated field element from constant limbs **without** /// in-circuit checks for field membership. If you want to enforce membership /// in the field, you can call `check_field_membership` on the output of this @@ -449,7 +508,7 @@ where CS: ConstraintSystem, { if self.is_constant() { - if BigInt::from(self) < P::modulus() { + if BigInt::try_from(self)? < P::modulus() { return Ok(()); } else { return Err(SynthesisError::Unsatisfiable); @@ -564,34 +623,44 @@ where ); return Err(SynthesisError::Unsatisfiable); } + let limb_count = a0.len(); let res_overflow = a1.overflow.max(a0.overflow); - let res_values = if condition.get_value().unwrap() { - match &a1.limbs { - EmulatedLimbs::Allocated(a1_var) => a1_var - .iter() - .map(|x| x.get_value().unwrap_or_default()) - .collect::>(), - EmulatedLimbs::Constant(a1_const) => a1_const.clone(), - } + let res_alloc_limbs = if let Some(cond) = condition.get_value() { + // If the condition has a value, then the limbs must also have a value, so we bubble + // the assignment missing error in this case + let res_values = if cond { + match &a1.limbs { + EmulatedLimbs::Allocated(a1_var) => a1_var + .iter() + .map(|v| v.get_value().ok_or(SynthesisError::AssignmentMissing)) + .collect::>()?, + EmulatedLimbs::Constant(a1_const) => a1_const.clone(), + } + } else { + match &a0.limbs { + EmulatedLimbs::Allocated(a0_var) => a0_var + .iter() + .map(|v| v.get_value().ok_or(SynthesisError::AssignmentMissing)) + .collect::>()?, + EmulatedLimbs::Constant(a0_const) => a0_const.clone(), + } + }; + EmulatedLimbs::allocate_limbs( + &mut cs.namespace(|| "allocate result limbs"), + &res_values, + ) } else { - match &a0.limbs { - EmulatedLimbs::Allocated(a0_var) => a0_var - .iter() - .map(|x| x.get_value().unwrap_or_default()) - .collect::>(), - EmulatedLimbs::Constant(a0_const) => a0_const.clone(), - } + // Otherwise, allocate "empty" limbs in case this is a MetricCS or TestCS + EmulatedLimbs::allocate_empty_limbs( + &mut cs.namespace(|| "allocate result limbs"), + limb_count, + )? }; - let res_alloc_limbs = EmulatedLimbs::allocate_limbs( - &mut cs.namespace(|| "allocate result limbs"), - &res_values, - ); - match &res_alloc_limbs { EmulatedLimbs::Allocated(res_limbs) => { - for i in 0..res_values.len() { + for i in 0..limb_count { let a1_lc = match &a1.limbs { EmulatedLimbs::Allocated(a1_var) => a1_var[i].lc(F::ONE), EmulatedLimbs::Constant(a1_const) => { diff --git a/crates/emulated/src/field_hints.rs b/crates/emulated/src/field_hints.rs index c9b9b28..6a25824 100644 --- a/crates/emulated/src/field_hints.rs +++ b/crates/emulated/src/field_hints.rs @@ -19,13 +19,13 @@ where where CS: ConstraintSystem, { - let a_int: BigInt = self.into(); - let p = P::modulus(); - let r_int = a_int.rem(p); - let r_value = Self::from(&r_int); + let a_int: Option = self.try_into().ok(); + let r_int = a_int.map(|v| v.rem(P::modulus())); - let res_limbs = - r_value.allocate_limbs(&mut cs.namespace(|| "allocate from remainder value"))?; + let res_limbs = Self::allocate_optional_limbs( + &mut cs.namespace(|| "allocate from remainder value"), + r_int, + )?; let res = Self::pack_limbs( &mut cs.namespace(|| "enforce bitwidths on remainder"), @@ -46,20 +46,26 @@ where + P::bits_per_limb() - 1) / // This term is to round up to next integer P::bits_per_limb(); - let a_int: BigInt = self.into(); + let a_int: Option = self.try_into().ok(); let p = P::modulus(); - let k_int = a_int.div(p); - let k_int_limbs = decompose(&k_int, P::bits_per_limb(), num_res_limbs)?; - - let res_limb_values: Vec = k_int_limbs - .into_iter() - .map(|i| bigint_to_scalar(&i)) - .collect::>(); - - let res_limbs = EmulatedLimbs::::allocate_limbs( - &mut cs.namespace(|| "allocate from quotient value"), - &res_limb_values, - ); + let k_int = a_int.map(|v| v.div(p)); + + let res_limbs = if let Some(k_int) = k_int { + let k_int_limbs = decompose(&k_int, P::bits_per_limb(), num_res_limbs)?; + let res_limb_values: Vec = k_int_limbs + .into_iter() + .map(|i| bigint_to_scalar(&i)) + .collect::>(); + EmulatedLimbs::::allocate_limbs( + &mut cs.namespace(|| "allocate from quotient value"), + &res_limb_values, + ) + } else { + EmulatedLimbs::::allocate_empty_limbs( + &mut cs.namespace(|| "allocate from quotient value"), + num_res_limbs, + )? + }; let res = Self::pack_limbs( &mut cs.namespace(|| "enforce bitwidths on quotient"), @@ -74,20 +80,22 @@ where where CS: ConstraintSystem, { - let mut a_int: BigInt = self.into(); let p = P::modulus(); - a_int = a_int.rem(&p); - if a_int.is_zero() { - eprintln!("Inverse of zero element cannot be calculated"); - return Err(SynthesisError::DivisionByZero); - } - let p_minus_2 = &p - BigInt::from(2); - // a^(p-1) = 1 mod p for non-zero a. So a^(-1) = a^(p-2) - let a_inv_int = a_int.modpow(&p_minus_2, &p); - let a_inv_value = Self::from(&a_inv_int); - - let a_inv_limbs = - a_inv_value.allocate_limbs(&mut cs.namespace(|| "allocate from inverse value"))?; + let a_int: Option = self.try_into().ok().map(|v: BigInt| v.rem(&p)); + let a_inv_int = a_int.and_then(|a| { + if a.is_zero() { + eprintln!("Inverse of zero element cannot be calculated"); + return None; + } + let p_minus_2 = &p - BigInt::from(2); + // a^(p-1) = 1 mod p for non-zero a. So a^(-1) = a^(p-2) + Some(a.modpow(&p_minus_2, &p)) + }); + + let a_inv_limbs = Self::allocate_optional_limbs( + &mut cs.namespace(|| "allocate from inverse value"), + a_inv_int, + )?; let a_inv = Self::pack_limbs( &mut cs.namespace(|| "enforce bitwidths on inverse"), @@ -107,23 +115,26 @@ where where CS: ConstraintSystem, { - let numer_int: BigInt = self.into(); - let mut denom_int: BigInt = other.into(); let p = P::modulus(); - denom_int = denom_int.rem(&p); - if denom_int.is_zero() { - eprintln!("Inverse of zero element cannot be calculated"); - return Err(SynthesisError::DivisionByZero); - } - let p_minus_2 = &p - BigInt::from(2); - // a^(p-1) = 1 mod p for non-zero a. So a^(-1) = a^(p-2) - let denom_inv_int = denom_int.modpow(&p_minus_2, &p); - let ratio_int = (numer_int * denom_inv_int).rem(&p); - - let ratio_value = Self::from(&ratio_int); - - let ratio_limbs = - ratio_value.allocate_limbs(&mut cs.namespace(|| "allocate from ratio value"))?; + let numer_int: Option = self.try_into().ok(); + let denom_int: Option = other.try_into().ok().map(|v: BigInt| v.rem(&p)); + let ratio_int = denom_int.and_then(|denom_int| { + numer_int.and_then(|numer_int| { + if denom_int.is_zero() { + eprintln!("Inverse of zero element cannot be calculated"); + return None; + } + let p_minus_2 = &p - BigInt::from(2); + // a^(p-1) = 1 mod p for non-zero a. So a^(-1) = a^(p-2) + let denom_inv_int = denom_int.modpow(&p_minus_2, &p); + Some((numer_int * denom_inv_int).rem(&p)) + }) + }); + + let ratio_limbs = Self::allocate_optional_limbs( + &mut cs.namespace(|| "allocate from ratio value"), + ratio_int, + )?; let ratio = Self::pack_limbs( &mut cs.namespace(|| "enforce bitwidths on ratio"), diff --git a/crates/emulated/src/field_ops.rs b/crates/emulated/src/field_ops.rs index f3b6261..83f37d6 100644 --- a/crates/emulated/src/field_ops.rs +++ b/crates/emulated/src/field_ops.rs @@ -128,19 +128,24 @@ where where CS: ConstraintSystem, { - let v_value = v.get_value().unwrap(); - let v_booleans = v_value - .to_le_bits() - .into_iter() - .skip(start_digit) - .take(end_digit - start_digit) - .enumerate() - .map(|(i, b)| { - Ok::(Boolean::from(AllocatedBit::alloc( - cs.namespace(|| format!("allocate bit {i}")), - Some(b), - )?)) - }); + let v_bits = if let Some(v_value) = v.get_value() { + v_value + .to_le_bits() + .into_iter() + .skip(start_digit) + .take(end_digit - start_digit) + .map(Some) + .collect() + } else { + vec![None; end_digit - start_digit] + }; + + let v_booleans = v_bits.into_iter().enumerate().map(|(i, b)| { + Ok::(Boolean::from(AllocatedBit::alloc( + cs.namespace(|| format!("allocate bit {i}")), + b, + )?)) + }); let mut sum_higher_order_bits = Num::::zero(); let mut sum_shifted_bits = Num::::zero(); @@ -176,8 +181,8 @@ where b.enforce_width_conditional(&mut cs.namespace(|| "ensure bitwidths in b"))?; if a.is_constant() && b.is_constant() { - let a_i = BigInt::from(a); - let b_i = BigInt::from(b); + let a_i = BigInt::try_from(a)?; + let b_i = BigInt::try_from(b)?; let a_r = a_i.rem(P::modulus()); let b_r = b_i.rem(P::modulus()); if a_r != b_r { @@ -214,8 +219,8 @@ where CS: ConstraintSystem, { if a.is_constant() && b.is_constant() { - let a_i = BigInt::from(a); - let b_i = BigInt::from(b); + let a_i = BigInt::try_from(a)?; + let b_i = BigInt::try_from(b)?; let a_r = a_i.rem(P::modulus()); let b_r = b_i.rem(P::modulus()); if a_r != b_r { @@ -328,15 +333,15 @@ where } } - fn add_op(a: &Self, b: &Self, next_overflow: usize) -> Self + fn add_op(a: &Self, b: &Self, next_overflow: usize) -> Result where CS: ConstraintSystem, { if a.is_constant() && b.is_constant() { - let a_int = BigInt::from(a); - let b_int = BigInt::from(b); + let a_int = BigInt::try_from(a)?; + let b_int = BigInt::try_from(b)?; let res_int = (a_int + b_int).rem(P::modulus()); - return Self::from(&res_int); + return Ok(Self::from(&res_int)); } let num_res_limbs = a.len().max(b.len()); @@ -373,7 +378,10 @@ where } } - Self::new_internal_element(EmulatedLimbs::Allocated(res), next_overflow) + Ok(Self::new_internal_element( + EmulatedLimbs::Allocated(res), + next_overflow, + )) } pub fn add(&self, cs: &mut CS, other: &Self) -> Result @@ -433,8 +441,8 @@ where CS: ConstraintSystem, { if a.is_constant() && b.is_constant() { - let a_int = BigInt::from(a); - let b_int = BigInt::from(b); + let a_int = BigInt::try_from(a)?; + let b_int = BigInt::try_from(b)?; let res_int = if a_int >= b_int { (a_int - b_int).rem(P::modulus()) } else { @@ -560,15 +568,14 @@ where CS: ConstraintSystem, { if a.is_constant() && b.is_constant() { - let a_int = BigInt::from(a); - let b_int = BigInt::from(b); + let a_int = BigInt::try_from(a)?; + let b_int = BigInt::try_from(b)?; let res_int = (a_int * b_int).rem(P::modulus()); return Ok(Self::from(&res_int)); } let num_prod_limbs = a.len() + b.len() - 1; let mut prod: Vec> = vec![Num::::zero(); num_prod_limbs]; - let mut prod_values: Vec = vec![F::ZERO; num_prod_limbs]; match (a.limbs.clone(), b.limbs.clone()) { (EmulatedLimbs::Constant(const_limbs), EmulatedLimbs::Allocated(var_limbs)) @@ -582,24 +589,35 @@ where } } (EmulatedLimbs::Allocated(a_var), EmulatedLimbs::Allocated(b_var)) => { - let a_var_limb_values: Vec = a_var + let a_var_limb_values: Option> = a_var .iter() - .map(|v| v.get_value().unwrap_or_default()) - .collect(); - let b_var_limb_values: Vec = b_var + .map(|v| v.get_value().ok_or(SynthesisError::AssignmentMissing)) + .collect::>() + .ok(); + let b_var_limb_values: Option> = b_var .iter() - .map(|v| v.get_value().unwrap_or_default()) - .collect(); - for i in 0..a.len() { - for j in 0..b.len() { - prod_values[i + j] += a_var_limb_values[i] * b_var_limb_values[j]; - } - } + .map(|v| v.get_value().ok_or(SynthesisError::AssignmentMissing)) + .collect::>() + .ok(); + let prod_values: Option> = a_var_limb_values.and_then(|a_var_limb_values| { + b_var_limb_values.map(|b_var_limb_values| { + let mut prod_values = vec![F::ZERO; num_prod_limbs]; + for i in 0..a.len() { + for j in 0..b.len() { + prod_values[i + j] += a_var_limb_values[i] * b_var_limb_values[j]; + } + } + prod_values + }) + }); let prod_allocated_nums: Vec> = (0..num_prod_limbs) .map(|i| { AllocatedNum::alloc(cs.namespace(|| format!("product limb {i}")), || { - Ok(prod_values[i]) + prod_values + .as_ref() + .map(|prod_values| prod_values[i]) + .ok_or(SynthesisError::AssignmentMissing) }) }) .collect::, _>>()?; @@ -857,7 +875,7 @@ where }; let res = match op_type { - Optype::Add => Ok(Self::add_op::(&a_r, &b_r, next_overflow)), + Optype::Add => Self::add_op::(&a_r, &b_r, next_overflow), Optype::Sub => Self::sub_op::(&a_r, &b_r, next_overflow), Optype::Mul => Self::mul_op(&mut cs.namespace(|| "mul_op"), &a_r, &b_r, next_overflow), }; diff --git a/crates/emulated/src/util.rs b/crates/emulated/src/util.rs index da28e7f..0221a9b 100644 --- a/crates/emulated/src/util.rs +++ b/crates/emulated/src/util.rs @@ -17,12 +17,7 @@ where F: PrimeFieldBits, CS: ConstraintSystem, { - range_check_lc( - cs, - &num.lc(F::ONE), - num.get_value().unwrap_or_default(), - num_bits, - ) + range_check_lc(cs, &num.lc(F::ONE), num.get_value(), num_bits) } /// Range check an expression represented by a LinearCombination @@ -31,14 +26,14 @@ where pub fn range_check_lc( cs: &mut CS, lc_input: &LinearCombination, - lc_value: F, + lc_value: Option, num_bits: usize, ) -> Result<(), SynthesisError> where F: PrimeFieldBits, CS: ConstraintSystem, { - let value_bits = lc_value.to_le_bits(); + let value_bits = lc_value.map(|v| v.to_le_bits()); // Allocate all but the first bit. let bits: Vec = (1..num_bits) @@ -46,8 +41,12 @@ where cs.alloc( || format!("bit {i}"), || { - let r = if value_bits[i] { F::ONE } else { F::ZERO }; - Ok(r) + if let Some(bits) = &value_bits { + let r = if bits[i] { F::ONE } else { F::ZERO }; + Ok(r) + } else { + Err(SynthesisError::AssignmentMissing) + } }, ) }) @@ -122,7 +121,7 @@ pub fn alloc_num_equals_constant>( ) -> Result { // Allocate and constrain `r`: result boolean bit. // It equals `true` if `a` equals `b`, `false` otherwise - let a_value = a.get_value().unwrap_or_default(); + let a_value = a.get_value().ok_or(SynthesisError::AssignmentMissing)?; let r = AllocatedBit::alloc(cs.namespace(|| "r"), Some(a_value == b))?; // Allocate t s.t. t=1 if a == b else 1/(a - b)