mirror of
https://github.com/logos-storage/plonky2.git
synced 2026-01-04 14:53:08 +00:00
curve multiply; test for curve add; addressed comments
This commit is contained in:
parent
70abf3e9cb
commit
284f9a412c
@ -1,6 +1,6 @@
|
||||
use std::ops::Mul;
|
||||
|
||||
use crate::curve::curve_summation::affine_summation_batch_inversion;
|
||||
use crate::curve::curve_summation::affine_multisummation_batch_inversion;
|
||||
use crate::curve::curve_types::{AffinePoint, Curve, CurveScalar, ProjectivePoint};
|
||||
use crate::field::field_types::Field;
|
||||
|
||||
@ -48,6 +48,7 @@ impl<C: Curve> ProjectivePoint<C> {
|
||||
|
||||
let mut y = ProjectivePoint::ZERO;
|
||||
let mut u = ProjectivePoint::ZERO;
|
||||
let mut all_summands = Vec::new();
|
||||
for j in (1..BASE).rev() {
|
||||
let mut u_summands = Vec::new();
|
||||
for (i, &digit) in digits.iter().enumerate() {
|
||||
@ -55,7 +56,12 @@ impl<C: Curve> ProjectivePoint<C> {
|
||||
u_summands.push(precomputed_powers[i]);
|
||||
}
|
||||
}
|
||||
u = u + affine_summation_batch_inversion(u_summands);
|
||||
all_summands.push(u_summands);
|
||||
}
|
||||
|
||||
let all_sums = affine_multisummation_batch_inversion(all_summands);
|
||||
for i in 0..all_sums.len() {
|
||||
u = u + all_sums[i];
|
||||
y = y + u;
|
||||
}
|
||||
y
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Neg;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::field::field_types::Field;
|
||||
|
||||
// To avoid implementation conflicts from associated types,
|
||||
@ -29,30 +27,6 @@ pub trait Curve: 'static + Sync + Sized + Copy + Debug {
|
||||
CurveScalar(x)
|
||||
}
|
||||
|
||||
/*fn try_convert_b2s(x: Self::BaseField) -> Result<Self::ScalarField> {
|
||||
x.try_convert::<Self::ScalarField>()
|
||||
}
|
||||
|
||||
fn try_convert_s2b(x: Self::ScalarField) -> Result<Self::BaseField> {
|
||||
x.try_convert::<Self::BaseField>()
|
||||
}
|
||||
|
||||
fn try_convert_s2b_slice(s: &[Self::ScalarField]) -> Result<Vec<Self::BaseField>> {
|
||||
let mut res = Vec::with_capacity(s.len());
|
||||
for &x in s {
|
||||
res.push(Self::try_convert_s2b(x)?);
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn try_convert_b2s_slice(s: &[Self::BaseField]) -> Result<Vec<Self::ScalarField>> {
|
||||
let mut res = Vec::with_capacity(s.len());
|
||||
for &x in s {
|
||||
res.push(Self::try_convert_b2s(x)?);
|
||||
}
|
||||
Ok(res)
|
||||
}*/
|
||||
|
||||
fn is_safe_curve() -> bool {
|
||||
// Added additional check to prevent using vulnerabilties in case a discriminant is equal to 0.
|
||||
(Self::A.cube().double().double() + Self::B.square().triple().triple().triple())
|
||||
@ -155,7 +129,7 @@ pub struct ProjectivePoint<C: Curve> {
|
||||
impl<C: Curve> ProjectivePoint<C> {
|
||||
pub const ZERO: Self = Self {
|
||||
x: C::BaseField::ZERO,
|
||||
y: C::BaseField::ZERO,
|
||||
y: C::BaseField::ONE,
|
||||
z: C::BaseField::ZERO,
|
||||
};
|
||||
|
||||
@ -166,7 +140,8 @@ impl<C: Curve> ProjectivePoint<C> {
|
||||
}
|
||||
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.to_affine().is_valid()
|
||||
let Self { x, y, z } = *self;
|
||||
z.is_zero() || y.square() * z == x.cube() + C::A * x * z.square() + C::B * z.cube()
|
||||
}
|
||||
|
||||
pub fn to_affine(&self) -> AffinePoint<C> {
|
||||
|
||||
@ -155,6 +155,17 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns x * y + z. This is no more efficient than mul-then-add; it's purely for convenience (only need to call one CircuitBuilder function).
|
||||
pub fn mul_add_biguint(
|
||||
&mut self,
|
||||
x: &BigUintTarget,
|
||||
y: &BigUintTarget,
|
||||
z: &BigUintTarget,
|
||||
) -> BigUintTarget {
|
||||
let prod = self.mul_biguint(x, y);
|
||||
self.add_biguint(&prod, z)
|
||||
}
|
||||
|
||||
pub fn div_rem_biguint(
|
||||
&mut self,
|
||||
a: &BigUintTarget,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use crate::curve::curve_types::{AffinePoint, Curve};
|
||||
use crate::field::extension_field::Extendable;
|
||||
use crate::field::field_types::RichField;
|
||||
use crate::field::field_types::{Field, RichField};
|
||||
use crate::gadgets::nonnative::NonNativeTarget;
|
||||
use crate::plonk::circuit_builder::CircuitBuilder;
|
||||
|
||||
@ -18,6 +18,17 @@ impl<C: Curve> AffinePointTarget<C> {
|
||||
}
|
||||
}
|
||||
|
||||
const WINDOW_BITS: usize = 4;
|
||||
const BASE: usize = 1 << WINDOW_BITS;
|
||||
|
||||
fn digits_per_scalar<C: Curve>() -> usize {
|
||||
(C::ScalarField::BITS + WINDOW_BITS - 1) / WINDOW_BITS
|
||||
}
|
||||
|
||||
pub struct MulPrecomputationTarget<C: Curve> {
|
||||
powers: Vec<AffinePointTarget<C>>,
|
||||
}
|
||||
|
||||
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
pub fn constant_affine_point<C: Curve>(
|
||||
&mut self,
|
||||
@ -39,6 +50,13 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
self.connect_nonnative(&lhs.y, &rhs.y);
|
||||
}
|
||||
|
||||
pub fn add_virtual_affine_point_target<C: Curve>(&mut self) -> AffinePointTarget<C> {
|
||||
let x = self.add_virtual_nonnative_target();
|
||||
let y = self.add_virtual_nonnative_target();
|
||||
|
||||
AffinePointTarget { x, y }
|
||||
}
|
||||
|
||||
pub fn curve_assert_valid<C: Curve>(&mut self, p: &AffinePointTarget<C>) {
|
||||
let a = self.constant_nonnative(C::A);
|
||||
let b = self.constant_nonnative(C::B);
|
||||
@ -61,11 +79,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn curve_double<C: Curve>(
|
||||
&mut self,
|
||||
p: &AffinePointTarget<C>,
|
||||
p_orig: AffinePoint<C>,
|
||||
) -> AffinePointTarget<C> {
|
||||
pub fn curve_double<C: Curve>(&mut self, p: &AffinePointTarget<C>) -> AffinePointTarget<C> {
|
||||
let AffinePointTarget { x, y } = p;
|
||||
let double_y = self.add_nonnative(y, y);
|
||||
let inv_double_y = self.inv_nonnative(&double_y);
|
||||
@ -89,6 +103,7 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
AffinePointTarget { x: x3, y: y3 }
|
||||
}
|
||||
|
||||
// Add two points, which are assumed to be non-equal.
|
||||
pub fn curve_add<C: Curve>(
|
||||
&mut self,
|
||||
p1: &AffinePointTarget<C>,
|
||||
@ -122,6 +137,110 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
y: y3_norm,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mul_precompute<C: Curve>(
|
||||
&mut self,
|
||||
p: &AffinePointTarget<C>,
|
||||
) -> MulPrecomputationTarget<C> {
|
||||
let num_digits = digits_per_scalar::<C>();
|
||||
|
||||
let mut powers = Vec::with_capacity(num_digits);
|
||||
powers.push(p.clone());
|
||||
for i in 1..num_digits {
|
||||
let mut power_i = powers[i - 1].clone();
|
||||
for _j in 0..WINDOW_BITS {
|
||||
power_i = self.curve_double(&power_i);
|
||||
}
|
||||
powers.push(power_i);
|
||||
}
|
||||
|
||||
MulPrecomputationTarget { powers }
|
||||
}
|
||||
|
||||
/*fn to_digits<C: Curve>(&mut self, x: &NonNativeTarget<C::ScalarField>) -> Vec<NonNativeTarget<C::ScalarField>> {
|
||||
debug_assert!(
|
||||
64 % WINDOW_BITS == 0,
|
||||
"For simplicity, only power-of-two window sizes are handled for now"
|
||||
);
|
||||
|
||||
let base = self.constant_nonnative(C::ScalarField::from_canonical_u64(BASE as u64));
|
||||
|
||||
let num_digits = digits_per_scalar::<C>();
|
||||
let mut digits = Vec::with_capacity(num_digits);
|
||||
|
||||
let (rest, limb) = self.div_rem_nonnative(&x, &base);
|
||||
for _ in 0..num_digits {
|
||||
digits.push(limb);
|
||||
|
||||
let (rest, limb) = self.div_rem_nonnative(&rest, &base);
|
||||
}
|
||||
|
||||
digits
|
||||
}
|
||||
|
||||
pub fn mul_with_precomputation<C: Curve>(
|
||||
&mut self,
|
||||
p: &AffinePointTarget<C>,
|
||||
n: &NonNativeTarget<C::ScalarField>,
|
||||
precomputation: MulPrecomputationTarget<C>,
|
||||
) -> AffinePointTarget<C> {
|
||||
// Yao's method; see https://koclab.cs.ucsb.edu/teaching/ecc/eccPapers/Doche-ch09.pdf
|
||||
let precomputed_powers = precomputation.powers;
|
||||
|
||||
let digits = self.to_digits(n);
|
||||
|
||||
|
||||
}*/
|
||||
|
||||
pub fn curve_scalar_mul<C: Curve>(
|
||||
&mut self,
|
||||
p: &AffinePointTarget<C>,
|
||||
n: &NonNativeTarget<C::ScalarField>,
|
||||
) -> AffinePointTarget<C> {
|
||||
let one = self.constant_nonnative(C::BaseField::ONE);
|
||||
let two = self.constant_nonnative(C::ScalarField::TWO);
|
||||
let num_bits = C::ScalarField::BITS;
|
||||
|
||||
// Result starts at p, which is later subtracted, because we don't support arithmetic with the zero point.
|
||||
let mut result = self.add_virtual_affine_point_target();
|
||||
self.connect_affine_point(p, &result);
|
||||
let mut two_i_times_p = self.add_virtual_affine_point_target();
|
||||
self.connect_affine_point(p, &two_i_times_p);
|
||||
|
||||
let mut cur_n = self.add_virtual_nonnative_target::<C::ScalarField>();
|
||||
for _i in 0..num_bits {
|
||||
let (bit_scalar, new_n) = self.div_rem_nonnative(&cur_n, &two);
|
||||
let bit_biguint = self.nonnative_to_biguint(&bit_scalar);
|
||||
let bit = self.biguint_to_nonnative::<C::BaseField>(&bit_biguint);
|
||||
let not_bit = self.sub_nonnative(&one, &bit);
|
||||
|
||||
let result_plus_2_i_p = self.curve_add(&result, &two_i_times_p);
|
||||
|
||||
let result_x = result.x;
|
||||
let result_y = result.y;
|
||||
let result_plus_2_i_p_x = result_plus_2_i_p.x;
|
||||
let result_plus_2_i_p_y = result_plus_2_i_p.y;
|
||||
|
||||
let new_x_if_bit = self.mul_nonnative(&bit, &result_plus_2_i_p_x);
|
||||
let new_x_if_not_bit = self.mul_nonnative(¬_bit, &result_x);
|
||||
let new_y_if_bit = self.mul_nonnative(&bit, &result_plus_2_i_p_y);
|
||||
let new_y_if_not_bit = self.mul_nonnative(¬_bit, &result_y);
|
||||
|
||||
let new_x = self.add_nonnative(&new_x_if_bit, &new_x_if_not_bit);
|
||||
let new_y = self.add_nonnative(&new_y_if_bit, &new_y_if_not_bit);
|
||||
|
||||
result = AffinePointTarget { x: new_x, y: new_y };
|
||||
|
||||
two_i_times_p = self.curve_double(&two_i_times_p);
|
||||
cur_n = new_n;
|
||||
}
|
||||
|
||||
// Subtract off result's intial value of p.
|
||||
let neg_p = self.curve_neg(&p);
|
||||
result = self.curve_add(&result, &neg_p);
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
mod tests {
|
||||
@ -200,19 +319,53 @@ mod tests {
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
let g = Secp256K1::GENERATOR_AFFINE;
|
||||
let neg_g = g.neg();
|
||||
let g_target = builder.constant_affine_point(g);
|
||||
let neg_g_target = builder.curve_neg(&g_target);
|
||||
|
||||
let double_g = g.double();
|
||||
let double_g_other_target = builder.constant_affine_point(double_g);
|
||||
builder.curve_assert_valid(&double_g_other_target);
|
||||
let double_g_expected = builder.constant_affine_point(double_g);
|
||||
builder.curve_assert_valid(&double_g_expected);
|
||||
|
||||
let double_g_target = builder.curve_double(&g_target, g);
|
||||
let double_neg_g_target = builder.curve_double(&neg_g_target, neg_g);
|
||||
let double_neg_g = (-g).double();
|
||||
let double_neg_g_expected = builder.constant_affine_point(double_neg_g);
|
||||
builder.curve_assert_valid(&double_neg_g_expected);
|
||||
|
||||
builder.curve_assert_valid(&double_g_target);
|
||||
builder.curve_assert_valid(&double_neg_g_target);
|
||||
let double_g_actual = builder.curve_double(&g_target);
|
||||
let double_neg_g_actual = builder.curve_double(&neg_g_target);
|
||||
builder.curve_assert_valid(&double_g_actual);
|
||||
builder.curve_assert_valid(&double_neg_g_actual);
|
||||
|
||||
builder.connect_affine_point(&double_g_expected, &double_g_actual);
|
||||
builder.connect_affine_point(&double_neg_g_expected, &double_neg_g_actual);
|
||||
|
||||
let data = builder.build();
|
||||
let proof = data.prove(pw).unwrap();
|
||||
|
||||
verify(proof, &data.verifier_only, &data.common)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_curve_add() -> Result<()> {
|
||||
type F = GoldilocksField;
|
||||
const D: usize = 4;
|
||||
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
|
||||
let pw = PartialWitness::new();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
|
||||
let g = Secp256K1::GENERATOR_AFFINE;
|
||||
let double_g = g.double();
|
||||
let g_plus_2g = (g + double_g).to_affine();
|
||||
let g_plus_2g_expected = builder.constant_affine_point(g_plus_2g);
|
||||
builder.curve_assert_valid(&g_plus_2g_expected);
|
||||
|
||||
let g_target = builder.constant_affine_point(g);
|
||||
let double_g_target = builder.curve_double(&g_target);
|
||||
let g_plus_2g_actual = builder.curve_add(&g_target, &double_g_target);
|
||||
builder.curve_assert_valid(&g_plus_2g_actual);
|
||||
|
||||
builder.connect_affine_point(&g_plus_2g_expected, &g_plus_2g_actual);
|
||||
|
||||
let data = builder.build();
|
||||
let proof = data.prove(pw).unwrap();
|
||||
|
||||
@ -17,6 +17,14 @@ pub struct NonNativeTarget<FF: Field> {
|
||||
}
|
||||
|
||||
impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
fn num_nonnative_limbs<FF: Field>() -> usize {
|
||||
let ff_size = FF::order();
|
||||
let f_size = F::order();
|
||||
let num_limbs = ((ff_size + f_size.clone() - BigUint::one()) / f_size).to_u32_digits()[0];
|
||||
|
||||
num_limbs as usize
|
||||
}
|
||||
|
||||
pub fn biguint_to_nonnative<FF: Field>(&mut self, x: &BigUintTarget) -> NonNativeTarget<FF> {
|
||||
NonNativeTarget {
|
||||
value: x.clone(),
|
||||
@ -42,6 +50,16 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
self.connect_biguint(&lhs.value, &rhs.value);
|
||||
}
|
||||
|
||||
pub fn add_virtual_nonnative_target<FF: Field>(&mut self) -> NonNativeTarget<FF> {
|
||||
let num_limbs = Self::num_nonnative_limbs::<FF>();
|
||||
let value = self.add_virtual_biguint_target(num_limbs);
|
||||
|
||||
NonNativeTarget {
|
||||
value,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
// Add two `NonNativeTarget`s.
|
||||
pub fn add_nonnative<FF: Field>(
|
||||
&mut self,
|
||||
@ -106,6 +124,20 @@ impl<F: RichField + Extendable<D>, const D: usize> CircuitBuilder<F, D> {
|
||||
inv
|
||||
}
|
||||
|
||||
pub fn div_rem_nonnative<FF: Field>(
|
||||
&mut self,
|
||||
x: &NonNativeTarget<FF>,
|
||||
y: &NonNativeTarget<FF>,
|
||||
) -> (NonNativeTarget<FF>, NonNativeTarget<FF>) {
|
||||
let x_biguint = self.nonnative_to_biguint(x);
|
||||
let y_biguint = self.nonnative_to_biguint(y);
|
||||
|
||||
let (div_biguint, rem_biguint) = self.div_rem_biguint(&x_biguint, &y_biguint);
|
||||
let div = self.biguint_to_nonnative(&div_biguint);
|
||||
let rem = self.biguint_to_nonnative(&rem_biguint);
|
||||
(div, rem)
|
||||
}
|
||||
|
||||
/// Returns `x % |FF|` as a `NonNativeTarget`.
|
||||
fn reduce<FF: Field>(&mut self, x: &BigUintTarget) -> NonNativeTarget<FF> {
|
||||
let modulus = FF::order();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user