Skip to content

Commit

Permalink
Add a test for the FCMP
Browse files Browse the repository at this point in the history
This verifies all constraints in the first layer evaluate to zero, as expected.

Various tweaks made as necessary for the test/to achieve this result.
  • Loading branch information
kayabaNerve committed May 4, 2024
1 parent ae34f0a commit 25bbb63
Show file tree
Hide file tree
Showing 11 changed files with 251 additions and 168 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion crypto/ciphersuite/src/helioselene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ impl Ciphersuite for Helios {
}
}


#[derive(Clone, Copy, PartialEq, Eq, Debug, Zeroize)]
pub struct Selene;
impl Ciphersuite for SelenePoint {
impl Ciphersuite for Selene {
type F = Field25519;
type G = SelenePoint;
type H = Blake2b512;
Expand Down
7 changes: 6 additions & 1 deletion crypto/divisors/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,19 @@ rustdoc-args = ["--cfg", "docsrs"]

[dependencies]
rand_core = { version = "0.6", default-features = false }

zeroize = { version = "^1.5", default-features = false, features = ["zeroize_derive"] }

group = "0.13"

hex = { version = "0.4", optional = true }
dalek-ff-group = { path = "../dalek-ff-group", features = ["std"], optional = true }

[dev-dependencies]
rand_core = { version = "0.6", features = ["getrandom"] }

hex = "0.4"
dalek-ff-group = { path = "../dalek-ff-group", features = ["std"] }
pasta_curves = { version = "0.5", default-features = false, features = ["bits", "alloc"], git = "https://github.com/kayabaNerve/pasta_curves.git" }

[features]
ed25519 = ["hex", "dalek-ff-group"]
59 changes: 59 additions & 0 deletions crypto/divisors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,62 @@ pub fn new_divisor<C: DivisorCurve>(points: &[C]) -> Option<Poly<C::FieldElement
// Return the unified divisor
Some(divs.remove(0).1)
}

#[cfg(any(test, feature = "ed25519"))]
mod ed25519 {
use group::{ff::{Field, PrimeField}, GroupEncoding};
use dalek_ff_group::{FieldElement, EdwardsPoint};

impl crate::DivisorCurve for EdwardsPoint {
type FieldElement = FieldElement;

// Wei25519 a/b
// https://www.ietf.org/archive/id/draft-ietf-lwig-curve-representations-02.pdf E.3
fn a() -> Self::FieldElement {
let mut be_bytes =
hex::decode("2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa984914a144").unwrap();
be_bytes.reverse();
let le_bytes = be_bytes;
Self::FieldElement::from_repr(le_bytes.try_into().unwrap()).unwrap()
}
fn b() -> Self::FieldElement {
let mut be_bytes =
hex::decode("7b425ed097b425ed097b425ed097b425ed097b425ed097b4260b5e9c7710c864").unwrap();
be_bytes.reverse();
let le_bytes = be_bytes;

Self::FieldElement::from_repr(le_bytes.try_into().unwrap()).unwrap()
}

// https://www.ietf.org/archive/id/draft-ietf-lwig-curve-representations-02.pdf E.2
fn to_xy(point: Self) -> (Self::FieldElement, Self::FieldElement) {
// Extract the y coordinate from the compressed point
let mut edwards_y = point.to_bytes();
let x_is_odd = edwards_y[31] >> 7;
edwards_y[31] &= (1 << 7) - 1;
let edwards_y = Self::FieldElement::from_repr(edwards_y).unwrap();

// Recover the x coordinate
let edwards_y_sq = edwards_y * edwards_y;
let D =
-Self::FieldElement::from(121665u64) * Self::FieldElement::from(121666u64).invert().unwrap();
let mut edwards_x = ((edwards_y_sq - Self::FieldElement::ONE) *
((D * edwards_y_sq) + Self::FieldElement::ONE).invert().unwrap())
.sqrt()
.unwrap();
if u8::from(bool::from(edwards_x.is_odd())) != x_is_odd {
edwards_x = -edwards_x;
}

// Calculate the x and y coordinates for Wei25519
let edwards_y_plus_one = Self::FieldElement::ONE + edwards_y;
let one_minus_edwards_y = Self::FieldElement::ONE - edwards_y;
let wei_x = (edwards_y_plus_one * one_minus_edwards_y.invert().unwrap()) +
(Self::FieldElement::from(486662u64) * Self::FieldElement::from(3u64).invert().unwrap());
let c =
(-(Self::FieldElement::from(486662u64) + Self::FieldElement::from(2u64))).sqrt().unwrap();
let wei_y = c * edwards_y_plus_one * (one_minus_edwards_y * edwards_x).invert().unwrap();
(wei_x, wei_y)
}
}
}
53 changes: 0 additions & 53 deletions crypto/divisors/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,59 +29,6 @@ impl DivisorCurve for Ep {
}
}

impl DivisorCurve for EdwardsPoint {
type FieldElement = dalek_ff_group::FieldElement;

// Wei25519 a/b
// https://www.ietf.org/archive/id/draft-ietf-lwig-curve-representations-02.pdf E.3
fn a() -> Self::FieldElement {
let mut be_bytes =
hex::decode("2aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa984914a144").unwrap();
be_bytes.reverse();
let le_bytes = be_bytes;
Self::FieldElement::from_repr(le_bytes.try_into().unwrap()).unwrap()
}
fn b() -> Self::FieldElement {
let mut be_bytes =
hex::decode("7b425ed097b425ed097b425ed097b425ed097b425ed097b4260b5e9c7710c864").unwrap();
be_bytes.reverse();
let le_bytes = be_bytes;

Self::FieldElement::from_repr(le_bytes.try_into().unwrap()).unwrap()
}

// https://www.ietf.org/archive/id/draft-ietf-lwig-curve-representations-02.pdf E.2
fn to_xy(point: Self) -> (Self::FieldElement, Self::FieldElement) {
// Extract the y coordinate from the compressed point
let mut edwards_y = point.to_bytes();
let x_is_odd = edwards_y[31] >> 7;
edwards_y[31] &= (1 << 7) - 1;
let edwards_y = Self::FieldElement::from_repr(edwards_y).unwrap();

// Recover the x coordinate
let edwards_y_sq = edwards_y * edwards_y;
let D =
-Self::FieldElement::from(121665u64) * Self::FieldElement::from(121666u64).invert().unwrap();
let mut edwards_x = ((edwards_y_sq - Self::FieldElement::ONE) *
((D * edwards_y_sq) + Self::FieldElement::ONE).invert().unwrap())
.sqrt()
.unwrap();
if u8::from(bool::from(edwards_x.is_odd())) != x_is_odd {
edwards_x = -edwards_x;
}

// Calculate the x and y coordinates for Wei25519
let edwards_y_plus_one = Self::FieldElement::ONE + edwards_y;
let one_minus_edwards_y = Self::FieldElement::ONE - edwards_y;
let wei_x = (edwards_y_plus_one * one_minus_edwards_y.invert().unwrap()) +
(Self::FieldElement::from(486662u64) * Self::FieldElement::from(3u64).invert().unwrap());
let c =
(-(Self::FieldElement::from(486662u64) + Self::FieldElement::from(2u64))).sqrt().unwrap();
let wei_y = c * edwards_y_plus_one * (one_minus_edwards_y * edwards_x).invert().unwrap();
(wei_x, wei_y)
}
}

fn test_divisor<C: DivisorCurve>() {
for i in 1 ..= 255 {
println!("Test iteration {i}");
Expand Down
8 changes: 7 additions & 1 deletion crypto/fcmps/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,10 @@ rand_core = { version = "0.6", features = ["getrandom"] }

transcript = { package = "flexible-transcript", path = "../transcript", features = ["recommended"] }

ciphersuite = { path = "../ciphersuite", features = ["ristretto"] }
ciphersuite = { path = "../ciphersuite", features = ["ed25519", "helioselene"] }

dalek-ff-group = { path = "../dalek-ff-group" }
helioselene = { path = "../helioselene" }

ec-divisors = { path = "../divisors", features = ["ed25519"] }
generalized-bulletproofs = { path = "../generalized-bulletproofs", features = ["tests"] }
21 changes: 8 additions & 13 deletions crypto/fcmps/src/circuit.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
use rand_core::{RngCore, CryptoRng};

use transcript::Transcript;

use ciphersuite::{
group::ff::{Field, PrimeField, PrimeFieldBits},
Ciphersuite,
};
use ciphersuite::{group::ff::Field, Ciphersuite};

use crate::lincomb::*;
use crate::gadgets::*;
Expand Down Expand Up @@ -81,20 +76,20 @@ impl<C: Ciphersuite> Circuit<C> {
let o = Variable::aO(self.muls);
self.muls += 1;

if let Some(a) = a {
self.constrain_equal_to_zero(a.term(-C::F::ONE, l));
}
if let Some(b) = b {
self.constrain_equal_to_zero(b.term(-C::F::ONE, r));
}

assert_eq!(self.prover.is_some(), witness.is_some());
if let Some(witness) = witness {
let prover = self.prover.as_mut().unwrap();
prover.aL.push(witness.0);
prover.aR.push(witness.1);
}

if let Some(a) = a {
self.constrain_equal_to_zero(a.term(-C::F::ONE, l));
}
if let Some(b) = b {
self.constrain_equal_to_zero(b.term(-C::F::ONE, r));
}

(l, r, o)
}

Expand Down
47 changes: 15 additions & 32 deletions crypto/fcmps/src/gadgets/interactive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub(crate) struct Divisor {
/// A claimed point and associated discrete logarithm claim.
#[derive(Clone, PartialEq, Eq, Debug)]
pub(crate) struct ClaimedPointWithDlog<F: Field> {
pub(crate) generator: (F, F),
pub(crate) generator: Vec<(F, F)>,
pub(crate) divisor: Divisor,
pub(crate) dlog: Vec<Variable>,
pub(crate) point: (Variable, Variable),
Expand Down Expand Up @@ -184,7 +184,7 @@ impl<C: Ciphersuite> Circuit<C> {
};

let p_1_n = two_c_y;
let p_1_d = (-slope * p_1_n) + three_x_sq_plus_a;
let p_1_d = (-slope * two_c_y) + three_x_sq_plus_a;

// Calculate the joint numerator
let p_n = p_0_n * p_1_n;
Expand Down Expand Up @@ -216,15 +216,15 @@ impl<C: Ciphersuite> Circuit<C> {
curve: &CurveSpec<C::F>,
claim: ClaimedPointWithDlog<C::F>,
) -> OnCurve {
let ClaimedPointWithDlog { mut generator, divisor, dlog, point } = claim;
assert_eq!(dlog.len(), <C::F as PrimeField>::NUM_BITS.try_into().unwrap());
dbg!("in discrete_log");
let ClaimedPointWithDlog { generator, divisor, dlog, point } = claim;

// Ensure this is being safely called
let arg_iter = core::iter::once(&divisor.y).chain(divisor.yx.iter());
let arg_iter =
arg_iter.chain(divisor.x_from_power_of_2.iter()).chain(core::iter::once(&divisor.zero));
let arg_iter = arg_iter.chain(dlog.iter());
for variable in arg_iter.chain(core::iter::once(&point.0)).chain(core::iter::once(&point.1)) {
for variable in arg_iter.chain([point.0, point.1].iter()) {
assert!(
matches!(variable, Variable::C(_, _)),
"discrete log requires all arguments belong to vector commitments",
Expand Down Expand Up @@ -256,56 +256,39 @@ impl<C: Ciphersuite> Circuit<C> {
break (c1_x, if bool::from(c1_y.is_odd()) { -c1_y } else { c1_y });
};

// mdbl-2007-bl
let double = |circuit: &mut Self, x: C::F, y: C::F| {
let xx = x * x;
let w = curve.a + (xx.double() + xx);
let y1y1 = y * y;
let r = y1y1.double();
let y1r = y * r;
let sss = y1r.double().double();
let rr = r * r;
let b = (x + r).square() - xx - rr;
let h = (w * w) - b.double();
let x3 = h.double() * y;
let y3 = (w * (b - h)) - rr.double();
let z3 = sss;

let z3_inv = circuit.divisor_challenge_invert(z3);
(x3 * z3_inv, y3 * z3_inv)
};

let (c2_x, c2_y) = incomplete_add::<C::F>(c0_x, c0_y, c1_x, c1_y)
.expect("couldn't perform incomplete addition on two distinct, on curve points");
let c2_y = -c2_y;

let slope = (c1_y - c0_y) * self.divisor_challenge_invert(c1_x - c0_x);
let intercept = c1_y - (slope * c1_x);

// lhs from the paper, evaluating the divisor
let mut eval = LinComb::from(self.divisor_challenge(curve, &divisor, slope, c0_x, c0_y)) +
let lhs_eval = LinComb::from(self.divisor_challenge(curve, &divisor, slope, c0_x, c0_y)) +
&LinComb::from(self.divisor_challenge(curve, &divisor, slope, c1_x, c1_y)) +
&LinComb::from(self.divisor_challenge(curve, &divisor, slope, c2_x, c2_y));

// Interpolate the powers of the generator
for bit in dlog {
let mut rhs_eval = LinComb::empty();
for (bit, generator) in dlog.into_iter().zip(generator) {
let weight = self.divisor_challenge_invert(intercept - (generator.1 - (slope * generator.0)));
eval = eval.term(-weight, bit);
// TODO: Take in a table as an argument
generator = double(self, generator.0, generator.1);
rhs_eval = rhs_eval.term(weight, bit);
}

// Interpolate the output point
// intercept - (y - (slope * x))
// intercept - y + (slope * x))
// -y + (slope * x)) + intercept
// EXCEPT the output point we're proving the discrete log for isn't the one interpolated
// Its negative is, so -y becomes y
let output_interpolation =
LinComb::empty().constant(intercept).term(-C::F::ONE, point.y).term(slope, point.x);
LinComb::empty().constant(intercept).term(C::F::ONE, point.y).term(slope, point.x);
let output_interpolation_eval = self.eval(&output_interpolation);
let (_output_interpolation, inverse) =
self.inverse(Some(output_interpolation), output_interpolation_eval);
eval = eval.term(-C::F::ONE, inverse);
rhs_eval = rhs_eval.term(C::F::ONE, inverse);

self.constrain_equal_to_zero(eval);
self.equality(lhs_eval, &rhs_eval);

point
}
Expand Down
Loading

0 comments on commit 25bbb63

Please sign in to comment.