Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/gadgets/bn254/fp254impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ pub trait Fp254Impl {
/// # Arguments
/// * `circuit` - Circuit to add gates to
/// * `a` - Wire in Montgomery form
/// * `b` - Constant in standard form
/// * `b` - Constant in Montgomery form
///
/// # Returns
/// Product in Montgomery form
Expand Down
12 changes: 7 additions & 5 deletions src/gadgets/bn254/fq2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,11 +312,8 @@ impl Fq2 {
) -> Fq2 {
assert_eq!(b.len(), Fq::N_BITS);

// Convert constant components to Montgomery so the result stays in Montgomery form
let a0_m = Fq::as_montgomery(a.c0);
let a1_m = Fq::as_montgomery(a.c1);
let c0 = Fq::mul_by_constant_montgomery(circuit, b, &a0_m);
let c1 = Fq::mul_by_constant_montgomery(circuit, b, &a1_m);
let c0 = Fq::mul_by_constant_montgomery(circuit, b, &a.c0);
let c1 = Fq::mul_by_constant_montgomery(circuit, b, &a.c1);

Fq2::from_components(c0, c1)
}
Expand Down Expand Up @@ -444,6 +441,11 @@ impl Fq2 {

Fq2::from_components(c0_final, c1_final)
}

pub fn conjugate<C: CircuitContext>(circuit: &mut C, a: &Fq2) -> Fq2 {
let new_c1 = Fq::neg(circuit, &a.c1().clone());
Fq2::from_components(a.c0().clone(), new_c1)
}
}

#[cfg(test)]
Expand Down
91 changes: 90 additions & 1 deletion src/gadgets/bn254/g1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use circuit_component_macro::component;
use crate::{
CircuitContext, WireId,
circuit::{FromWires, WiresObject},
gadgets::bn254::{fp254impl::Fp254Impl, fq::Fq, fr::Fr},
gadgets::{bigint, bn254::{fp254impl::Fp254Impl, fq::Fq, fr::Fr}},
};

#[derive(Clone, Debug)]
Expand Down Expand Up @@ -398,6 +398,23 @@ impl G1Projective {
z: p.z.clone(),
}
}

/// check whether or not the point is on the curve or not
/// checks y^2=x^3+3z^6 (Jacobian projective coordinates)
#[component]
pub fn is_on_curve<C: CircuitContext>(circuit: &mut C, p: &G1Projective) -> WireId {
let x2 = Fq::square_montgomery(circuit, &p.x);
let x3 = Fq::mul_montgomery(circuit, &p.x, &x2);
let y2 = Fq::square_montgomery(circuit, &p.y);
let z2 = Fq::square_montgomery(circuit, &p.z);
let z4 = Fq::square_montgomery(circuit, &z2);
let z6 = Fq::mul_montgomery(circuit, &z2, &z4);
let triplez6 = Fq::triple(circuit, &z6);
let temp = Fq::add(circuit, &x3, &triplez6);
let should_be_zero = Fq::sub(circuit, &y2, &temp);
let result = bigint::equal_zero(circuit, &should_be_zero.0);
result
}
}

#[cfg(test)]
Expand Down Expand Up @@ -842,4 +859,76 @@ mod tests {
let actual_result = G1Projective::from_bits_unchecked(result.output_value.clone());
assert_eq!(actual_result, neg_a_mont);
}

#[test]
fn test_g1p_is_on_curve() {
// Generate random G1 points, a is on curve, b isn't
let a = rnd();
let b = ark_bn254::G1Projective {
x: Fq::random(&mut trng()),
y: Fq::random(&mut trng()),
z: Fq::random(&mut trng()),
};

// Convert to Montgomery form
let a_mont = G1Projective::as_montgomery(a);
let b_mont = G1Projective::as_montgomery(b);

// Define input structure
struct OneG1Input {
a: ark_bn254::G1Projective,
}
struct OneG1InputWire {
a: G1Projective,
}
impl crate::circuit::CircuitInput for OneG1Input {
type WireRepr = OneG1InputWire;
fn allocate(&self, issue: impl FnMut() -> WireId) -> Self::WireRepr {
OneG1InputWire {
a: G1Projective::new(issue),
}
}
fn collect_wire_ids(repr: &Self::WireRepr) -> Vec<WireId> {
let mut wires = Vec::new();
wires.extend(repr.a.x.iter());
wires.extend(repr.a.y.iter());
wires.extend(repr.a.z.iter());
wires
}
}
impl<M: CircuitMode<WireValue = bool>> EncodeInput<M> for OneG1Input {
fn encode(&self, repr: &OneG1InputWire, cache: &mut M) {
let a_fn = G1Projective::get_wire_bits_fn(&repr.a, &self.a).unwrap();
for &wire_id in repr
.a
.x
.iter()
.chain(repr.a.y.iter())
.chain(repr.a.z.iter())
{
if let Some(bit) = a_fn(wire_id) {
cache.feed_wire(wire_id, bit);
}
}
}
}

let inputs = OneG1Input { a: a_mont };
let result: crate::circuit::StreamingResult<_, _, Vec<bool>> =
CircuitBuilder::streaming_execute(inputs, 10_000, |root, inputs_wire| {
let result = G1Projective::is_on_curve(root, &inputs_wire.a);
vec![result]
});

assert!(result.output_value.clone()[0]);

let inputs = OneG1Input { a: b_mont };
let result: crate::circuit::StreamingResult<_, _, Vec<bool>> =
CircuitBuilder::streaming_execute(inputs, 10_000, |root, inputs_wire| {
let result = G1Projective::is_on_curve(root, &inputs_wire.a);
vec![result]
});

assert!(!result.output_value.clone()[0]);
}
}
161 changes: 157 additions & 4 deletions src/gadgets/bn254/g2.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{cmp::min, collections::HashMap, iter::zip};

use ark_ff::Zero;
use ark_ec::bn::BnConfig;
use ark_ff::{AdditiveGroup, Zero};
use circuit_component_macro::component;

use crate::{
Expand Down Expand Up @@ -496,6 +497,56 @@ impl G2Projective {
acc
}

// #[component(offcircuit_args = "s")]
pub fn scalar_mul_by_constant_scalar_montgomery<C: CircuitContext, const W: usize>(
circuit: &mut C,
s: &u64,
base: &G2Projective,
) -> G2Projective {

fn scalar_pieces(window: usize, scalar: u64) -> Vec<u64> {
let mut a = scalar;
let c = (1 << window) - 1;
let mut pieces = Vec::new();

while a != 0 {
pieces.push(a & c);
a >>= window;
}
pieces.reverse();
pieces
}

let n = 2_usize.pow(W as u32);

let mut bases = Vec::new();
let mut p = G2Projective::new_constant(&G2Projective::as_montgomery(
ark_bn254::G2Projective::default(),
))
.unwrap();

for _ in 0..n {
bases.push(p.clone());
p = G2Projective::add_montgomery(circuit, &p, &base);
}

let pieces = scalar_pieces(W, *s);

let mut acc = G2Projective::new_constant(&G2Projective::as_montgomery(
ark_bn254::G2Projective::default(),
))
.unwrap();

for piece in pieces {
for _ in 0..W {
acc = G2Projective::double_montgomery(circuit, &acc);
}
acc = G2Projective::add_montgomery(circuit, &acc, &bases[piece as usize]);
}

acc
}

pub fn msm_with_constant_bases_montgomery<const W: usize, C: CircuitContext>(
circuit: &mut C,
scalars: &Vec<Fr>,
Expand Down Expand Up @@ -524,12 +575,46 @@ impl G2Projective {
z: p.z.clone(),
}
}

#[component]
pub fn psi_montgomery<C: CircuitContext>(circuit: &mut C, p: &G2Projective) -> G2Projective {
let a = ark_bn254::Config::TWIST_MUL_BY_Q_X;
let b = ark_bn254::Config::TWIST_MUL_BY_Q_Y;
let y_conjugate = Fq2::conjugate(circuit, &p.y);
let new_y = Fq2::mul_by_constant_montgomery(circuit, &y_conjugate, &Fq2::as_montgomery(b));
let x_conjugate = Fq2::conjugate(circuit, &p.x);
let new_x = Fq2::mul_by_constant_montgomery(circuit, &x_conjugate, &Fq2::as_montgomery(a));
let new_p = G2Projective {
x: new_x,
y: new_y,
z: p.z.clone(),
};
new_p
}

#[component]
pub fn is_r_torsion_montgomery<C: CircuitContext>(circuit: &mut C, p: &G2Projective) -> WireId {
let x = 4965661367192848881;
let xp = G2Projective::scalar_mul_by_constant_scalar_montgomery::<_, 2>(circuit, &x, p);
let xp_plus_p = G2Projective::add_montgomery(circuit, &xp, p);
// ψ([x₀]P) + ψ²([x₀]P) - ψ³([2x₀]P)
let psi_1_xp = G2Projective::psi_montgomery(circuit, &xp);
let psi_2_xp = G2Projective::psi_montgomery(circuit, &psi_1_xp);
let psi_3_xp = G2Projective::psi_montgomery(circuit, &psi_2_xp);
let psi_3_xp_double = G2Projective::double_montgomery(circuit, &psi_3_xp);
let psi_3_xp_double_neg = G2Projective::neg(circuit, &psi_3_xp_double);
let a = G2Projective::add_montgomery(circuit, &psi_1_xp, &psi_2_xp);
let b = G2Projective::add_montgomery(circuit, &a, &psi_3_xp_double_neg);
let c = G2Projective::add_montgomery(circuit, &b, &xp_plus_p);
let result = Fq2::equal_constant(circuit, &c.z, &ark_bn254::Fq2::ZERO);
result
}
}

#[cfg(test)]
mod tests {
use ark_ec::{CurveGroup, VariableBaseMSM};
use ark_ff::UniformRand;
use ark_ec::{CurveGroup, VariableBaseMSM, short_weierstrass::SWCurveConfig};
use ark_ff::{Field, UniformRand};
use rand::{Rng, SeedableRng};
use rand_chacha::ChaCha20Rng;

Expand Down Expand Up @@ -794,7 +879,7 @@ mod tests {
}

#[test]
fn test_g2p_scalar_mul_with_constant_base_montgomery() {
fn test_g2p_scalar_mul_by_constant_base_montgomery() {
let s = rnd_fr(&mut trng());
let p = rnd_g2(&mut trng());
let result = p * s;
Expand All @@ -814,6 +899,30 @@ mod tests {
assert_eq!(actual_result, G2Projective::as_montgomery(result));
}

#[test]
fn test_g2p_scalar_mul_by_constant_scalar_montgomery() {
let x = 4965661367192848881;
let s = x;
let p = rnd_g2(&mut trng());
let result = p * ark_bn254::Fr::from(s);

let p_mont = G2Projective::as_montgomery(p);

let inputs = G2Input { points: [p_mont] };
let circuit_result: crate::circuit::StreamingResult<_, _, Vec<bool>> =
CircuitBuilder::streaming_execute(inputs, 10_000, |root, inputs_wire| {
let result_wires = G2Projective::scalar_mul_by_constant_scalar_montgomery::<_, 2>(
root,
&s,
&inputs_wire.points[0],
);
result_wires.to_wires_vec()
});

let actual_result = G2Projective::from_bits_unchecked(circuit_result.output_value.clone());
assert_eq!(actual_result, G2Projective::as_montgomery(result));
}

#[test]
fn test_msm_with_constant_bases_montgomery() {
let n = 1;
Expand Down Expand Up @@ -873,4 +982,48 @@ mod tests {
let actual_result = G2Projective::from_bits_unchecked(circuit_result.output_value.clone());
assert_eq!(actual_result, G2Projective::as_montgomery(result));
}

#[test]
fn test_g2p_is_r_torsion_montgomery() {
// a point which is in r-torsion subgroup G2
let p = rnd_g2(&mut trng());
assert!(p.into_affine().is_on_curve());
assert!(p.into_affine().is_in_correct_subgroup_assuming_on_curve());

let p_mont = G2Projective::as_montgomery(p);

let inputs = G2Input { points: [p_mont] };
let circuit_result: crate::circuit::StreamingResult<_, _, Vec<bool>> =
CircuitBuilder::streaming_execute(inputs, 10_000, |root, inputs_wire| {
let result_wires = G2Projective::is_r_torsion_montgomery(root, &inputs_wire.points[0]);
result_wires.to_wires_vec()
});

assert!(circuit_result.output_value[0].clone());

// a point which is NOT in r-torsion subgroup G2
let px = Fq2::random(&mut trng());
let px2 = px.square();
let px3 = px2 * px;
let py2 = px3 + ark_bn254::g2::Config::COEFF_B;
let py = py2.sqrt().unwrap();
let p = ark_bn254::G2Projective {
x: px,
y: py,
z: ark_bn254::Fq2::ONE
};
assert!(p.into_affine().is_on_curve());
assert!(!p.into_affine().is_in_correct_subgroup_assuming_on_curve());

let p_mont = G2Projective::as_montgomery(p);

let inputs = G2Input { points: [p_mont] };
let circuit_result: crate::circuit::StreamingResult<_, _, Vec<bool>> =
CircuitBuilder::streaming_execute(inputs, 10_000, |root, inputs_wire| {
let result_wires = G2Projective::is_r_torsion_montgomery(root, &inputs_wire.points[0]);
result_wires.to_wires_vec()
});

assert!(!circuit_result.output_value[0].clone());
}
}
Loading