Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Ginger-Lib #144

Closed
wants to merge 88 commits into from
Closed

Refactor Ginger-Lib #144

wants to merge 88 commits into from

Conversation

phoinic
Copy link

@phoinic phoinic commented Dec 9, 2021

This PR contains the refactored algebra module and adjustments for this refactoring in other modules. The details of this refactoring are described in document "Refactoring ginger-lib". Key changes:

  • Group trait is a basic trait for Fields, Polynomials and Curves
  • Curve trait introduced
  • Projective representation for curve points is default
  • Affine representation used only for efficient mixed arithmetic and moved to the Curve trait as a nested type
  • All curves removed except Tweedle and secp256k1
  • All curves and fields models which are not used be Tweedle and secp256k1 are removed

Known Issues:

  1. In algebra / secp256k1 unit tests there are hardcoded points in affine representation. Correct values for Jacobian representation should be placed. For now these UTs are broken and marked as ignored

  2. In primitives / merkle_tree / optimized the UTs were switched from MNT curve to tweedle. The hardcoded expected output values should be updated with correct ones. For now these UTs are broken and marked as ignored

  3. In proof-systems / darlin / tests inside TestCircuit there is a comment:
    // This calculation is wrong, as enforce_equal allocates new variables.
    // However, fixing this may cause unit tests to crash since num_constraints
    // and num_variables are generated at random and an underflow may happen.
    // Fix both
    UTs can randomly failed or pass this part with error "substract with overflow"

  4. Inside r1cs / gadgets / crypto / crh / pedersen the unit-test was switched from JubJub to Tweedle. But with tweedle this UT failed for some reason. This is not a refactoring related problem because in case of changing JubJub to Tweedle inside none-refactored ginger-lib we have the same issue with this UT. For now UT is marked as ignored

Copy link

@UlrichHaboeck75 UlrichHaboeck75 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this is a quite cross-over pull request, it's hard to give a summary. Please see my comments below.

README.md Outdated Show resolved Hide resolved
algebra/src/curves/check_curve_parameters.sage Outdated Show resolved Hide resolved
Comment on lines 63 to 71
#### Checking if the file contains Short Weierstrass parameters. If not, the check is interrupted.
#### If there are Twisted Edwards and Montgomery parameters, they are discarded.
if 'SWModelParameters' in readfile:
if 'TEModelParameters' in readfile:
to_be_removed = readfile[readfile.index('impl TEModelParameters'):readfile.index('impl SWModelParameters')]
readfile = readfile.replace(to_be_removed,'')
else:
print("WARNING! THE FILE HAS NO SHORT WEIERSTRASS PARAMETERS!")
sys.exit()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DDT92 for now the script is only for SWModelParameters. Would be cool to handle TEModelParameters and MontgomeryModelParameters too!

algebra/src/curves/ed25519/mod.rs Outdated Show resolved Hide resolved
algebra/src/curves/ed25519/mod.rs Outdated Show resolved Hide resolved
Ok(())
let mut power = self.clone();
let mut acc = Self::zero(cs.ns(|| "alloc acc"))?;
for (i, bit) in bits.enumerate() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the ordinary double and add. We should check if there are improvements as we have for Weierstrass curves.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a TODO

CS: ConstraintSystemAbstract<ConstraintF>,
T: 'a + ToBitsGadget<ConstraintF> + ?Sized,
I: Iterator<Item = &'a T>,
B: Borrow<[TEExtended<P>]>,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As for the other msm gadgets. See my comments there.

@@ -1302,11 +914,12 @@ mod projective_impl {
) -> Result<Self, SynthesisError>
where
FN: FnOnce() -> Result<T, SynthesisError>,
T: Borrow<TEProjective<P>>,
T: Borrow<TEExtended<P>>,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A general comment: why do we enforce curve membership for an input?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably a leftover, but we do it also for SW curves. Can we safely remove such checks ?

use num_traits::Zero;
use serde::*;

#[derive(Clone, PartialEq, Eq, Debug, Hash, CanonicalSerialize, CanonicalDeserialize, Serialize, Deserialize)]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue of trailing zeros pops up also regarding the PartialEq and Eq traits, which cannot be derived if we want to correctly take into account trailing zeros.
I would suggest the following implementation instead (disclaimer: untested and adds a dependency to the crate itertools)

// The trait PartialEq cannot be simply derived, because this would cause wrong results when
// comparing vectors which are equal apart from a different number of trailing zeros (in
// particular when comparing different representations of the zero vector).
impl<G: Group> PartialEq<Self> for GroupVec<G> {
    fn eq(&self, other: &Self) -> bool {
        self.0
            .iter()
            .zip_longest(other.0.iter())
            .all(|elems| match elems {
                EitherOrBoth::Both(g1, g2) => g1 == g2,
                EitherOrBoth::Left(g) => g.is_zero(),
                EitherOrBoth::Right(g) => g.is_zero(),
            })
    }
}

impl<G: Group> Eq for GroupVec<G> {}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me, I would enforce the comparison only with Polynomials/GroupVec of the same length.
If they aren't they are not equal.
If they are, and they have trailing zeros, then they must have the same amount of trailing zeros

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do it. This 0 repr for Polynomial and GroupVec only affects the DomainExtendedPolyCommit.
For the moment let's handle problems individually. When we are going to refactor, removing the DomainExtended and assuming that Commitments are always segmented, probably we can do better.

use std::ops::{Add, AddAssign, Deref, DerefMut, Div, Mul, Neg, Sub, SubAssign};
use std::io::{Read, Result as IoResult, Write};
use std::ops::{Add, AddAssign, Deref, DerefMut, Div, Mul, MulAssign, Neg, Sub, SubAssign};
use num_traits::Zero;

/// Stores a polynomial in coefficient form.
#[derive(Clone, PartialEq, Eq, Hash, Default, CanonicalSerialize, CanonicalDeserialize)]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that deriving PartialEq and Eq for DensePolynomial leads to the same issue as for GroupVec concerning the possible presence of trailing zeros. The problem could be solved in a similar way too.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll do. See my answer abovee

Comment on lines +15 to +18
pub trait PedersenWindow: Clone {
const WINDOW_SIZE: usize;
const NUM_WINDOWS: usize;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let us document here the definition of WINDOW_SIZE and NUM_WINDOWS, including security conditions.

@@ -29,6 +29,10 @@ pub mod tests;
pub use self::models::*;

/// Projective representation of an elliptic curve point.
/// This trait t serves curve-specific functions not covered

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo? This trait t ...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, thanks

@@ -71,10 +75,10 @@ pub trait Curve:

/// Convert, if possible, a batch of `self` points to their affine equivalent.
#[inline]
fn batch_into_affine<'a>(vec_self: &'a [Self]) -> Result<Vec<Self::AffineRep>, Error> {
fn batch_into_affine(vec_self: Vec<Self>) -> Result<Vec<Self::AffineRep>, Error> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason for having a Vec instead of a slice, as in batch_from_affine()?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logically yes: you are consuming Self to obtain an Affine (all the usages were like this in the end, and I don't see any reason why Projective and Affine are allowed to co-exist in the same piece of code at a given moment)


// Force deserialize test
{
let a = Jacobian::<P>::rand(rng);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we resample a?

(Not the same, but related: Why is a set zero in Line 373?)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No particular reason, just to have more randomness. About the second comment a is not set to zero, are you looking at the correct diff ?

Comment on lines +357 to +362
for _ in 0..ITERATIONS {
let g = Jacobian::<P>::rand(&mut rng);
let g_affine = g.try_into().unwrap();
let g_projective = Jacobian::<P>::from_affine(&g_affine);
assert_eq!(g, g_projective);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if the sampled g is the zero element? Is this excluded by our implementation of the rng?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rng works by sampling a random x and then plug it in the curve equation. I don't think it's possible to sample infinity

@@ -137,6 +160,16 @@ impl<G: Group> Neg for GroupVec<G> {
}
}

impl<'a, G: Group> Neg for &'a GroupVec<G> {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What exactly is the reason for this extra implementation of Neg (and the other arithmetic traits below)?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I needed mainly for re-using the group tests. Plus they don't harm, they provide additional capabilities

Comment on lines 326 to 329
let (a, mut b) = {
let rng = &mut thread_rng();
(GroupVec::<TweedleDee>::rand(MAX_LENGTH, rng), GroupVec::<TweedleDee>::rand(MAX_LENGTH, rng))
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be more approriate to sample group vecs of different lengths?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}

/// Initialize a random LC from a single batching scalar and a vector of points and checks that the result of combine is indeed
/// given by (0 * point_0) + (scalar * point_1) + (scalar^2 * point_2) + ... + (scalar^n-1 * point_n-1)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given by (1 * point_0) + ...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, done

Comment on lines +177 to +182
let scalars = (0..MAX_LENGTH)
.map(|_| Fr::rand(rng))
.collect::<Vec<_>>();
let points = (0..MAX_LENGTH)
.map(|_| TweedleDee::rand(rng))
.collect::<Vec<_>>();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not choosing a different parameter for the number of samples? As I understand MAX_LENGTH is the length of a group vec, and typically a quite small number.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is set to 10 currently, it's good enough I think

Comment on lines 11 to 17
/// Given base points `G[i]` together with scalars `c[i]`, `i=1,..n`, we write
/// c[i] = sum c[i,j] * D^j,
/// s[j] = Sum_i c[i,j] * G[i],
/// for each order `j`.
/// The final result of the multi-scalar multiplication is then combined by
/// s = Sum_j A^j * s[j],
/// using double-and-add.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me, this shortened explanation hides that the bucket method is applied to the digits from D.
How about

/// Given base points `G[i]` together with scalars `c[i]`, `i=1,..n`, we write 
///    c[i] = sum  c[i,j] * D^j,
/// with digits `c[i,j]` from the interval `{0,...,D-1}`, and compute 
///    s[j] = Sum_i c[i,j] * G[i],
/// for each order `j` using the Pippenger-Yao bucket method.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine thanks !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants