diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c3034ecd..4a36e215f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # CHANGELOG ## Pending +- [\#926](https://github.com/arkworks-rs/algebra/pull/926) Add the circom compatible Baby Jubjub curve ### Breaking changes diff --git a/curves/Cargo.toml b/curves/Cargo.toml index ef7dae6e3..382200db7 100644 --- a/curves/Cargo.toml +++ b/curves/Cargo.toml @@ -3,6 +3,7 @@ members = [ "curve-constraint-tests", + "baby_jubjub", "bls12_377", "ed_on_bls12_377", diff --git a/curves/baby_jubjub/Cargo.toml b/curves/baby_jubjub/Cargo.toml new file mode 100644 index 000000000..07e85b87e --- /dev/null +++ b/curves/baby_jubjub/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "ark-babyjubjub" +version.workspace = true +authors.workspace = true +description = "The Baby Jubjub Twisted Edwards curve" +homepage.workspace = true +repository.workspace = true +documentation = "https://docs.rs/ark-babyjubjub/" +keywords.workspace = true +categories.workspace = true +include.workspace = true +license.workspace = true +edition.workspace = true + +[dependencies] +ark-ff = { workspace = true } +ark-ec = { workspace = true } +ark-std = { workspace = true } +ark-r1cs-std = { workspace = true, optional = true } +ark-bn254 = { workspace = true, features = [ "scalar_field" ] } + +[dev-dependencies] +ark-relations = { workspace = true } +ark-serialize = { workspace = true } +ark-algebra-test-templates = { workspace = true } +ark-curve-constraint-tests = { path = "../curve-constraint-tests" } + +[features] +default = [] +std = [ "ark-std/std", "ark-ff/std", "ark-ec/std", "ark-bn254/std" ] +r1cs = ["ark-r1cs-std"] diff --git a/curves/baby_jubjub/LICENSE-APACHE b/curves/baby_jubjub/LICENSE-APACHE new file mode 120000 index 000000000..965b606f3 --- /dev/null +++ b/curves/baby_jubjub/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/curves/baby_jubjub/LICENSE-MIT b/curves/baby_jubjub/LICENSE-MIT new file mode 120000 index 000000000..76219eb72 --- /dev/null +++ b/curves/baby_jubjub/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/curves/baby_jubjub/src/constraints/curves.rs b/curves/baby_jubjub/src/constraints/curves.rs new file mode 100644 index 000000000..fffc1a3c3 --- /dev/null +++ b/curves/baby_jubjub/src/constraints/curves.rs @@ -0,0 +1,11 @@ +use ark_r1cs_std::groups::curves::twisted_edwards::AffineVar; + +use crate::{constraints::FqVar, *}; + +/// A variable that is the R1CS equivalent of `crate::EdwardsAffine`. +pub type EdwardsVar = AffineVar; + +#[test] +fn test() { + ark_curve_constraint_tests::curves::te_test::().unwrap(); +} diff --git a/curves/baby_jubjub/src/constraints/fields.rs b/curves/baby_jubjub/src/constraints/fields.rs new file mode 100644 index 000000000..3f81d7a3f --- /dev/null +++ b/curves/baby_jubjub/src/constraints/fields.rs @@ -0,0 +1,9 @@ +use ark_r1cs_std::fields::fp::FpVar; + +/// A variable that is the R1CS equivalent of `crate::Fq`. +pub type FqVar = FpVar; + +#[test] +fn test() { + ark_curve_constraint_tests::fields::field_test::<_, _, FqVar>().unwrap(); +} diff --git a/curves/baby_jubjub/src/constraints/mod.rs b/curves/baby_jubjub/src/constraints/mod.rs new file mode 100644 index 000000000..37638b2a9 --- /dev/null +++ b/curves/baby_jubjub/src/constraints/mod.rs @@ -0,0 +1,8 @@ +//! This module implements the R1CS equivalent of `ark_babyjubjub`. +//! It requires a curve that embeds Baby Jubjub curve. + +mod curves; +mod fields; + +pub use curves::*; +pub use fields::*; diff --git a/curves/baby_jubjub/src/curves/mod.rs b/curves/baby_jubjub/src/curves/mod.rs new file mode 100644 index 000000000..d3570018a --- /dev/null +++ b/curves/baby_jubjub/src/curves/mod.rs @@ -0,0 +1,77 @@ +use ark_ec::{ + models::CurveConfig, + twisted_edwards::{Affine, MontCurveConfig, Projective, TECurveConfig}, +}; +use ark_ff::{Field, MontFp}; + +use crate::{Fq, Fr}; + +#[cfg(test)] +mod tests; + +pub type EdwardsAffine = Affine; +pub type EdwardsProjective = Projective; + +/// `Baby-JubJub` is a twisted Edwards curve. These curves have equations of the +/// form: ax² + y² = 1 + dx²y². +/// over some base finite field Fq. +/// +/// Baby-JubJub's curve equation: Ax^2 + y^2 = 1 + Dx^2y^2, where +/// * A = 168700 +/// * D = 168696 +/// +/// q = 21888242871839275222246405745257275088548364400416034343698204186575808495617 +#[derive(Clone, Default, PartialEq, Eq)] +pub struct EdwardsConfig; + +impl CurveConfig for EdwardsConfig { + type BaseField = Fq; + type ScalarField = Fr; + + /// COFACTOR = 8 + const COFACTOR: &'static [u64] = &[8]; + + /// COFACTOR^(-1) mod r = + /// 2394026564107420727433200628387514462817212225638746351800188703329891451411 + const COFACTOR_INV: Fr = + MontFp!("2394026564107420727433200628387514462817212225638746351800188703329891451411"); +} + +impl TECurveConfig for EdwardsConfig { + /// COEFF_A = 168700 + const COEFF_A: Fq = MontFp!("168700"); + + /// COEFF_D = 168696 + const COEFF_D: Fq = MontFp!("168696"); + + /// Standard base points from https://eips.ethereum.org/EIPS/eip-2494. + /// Note: A base point B is used instead of a generator G satisfying B = 8 * G. + /// The Montgomery form is + /// x = 7, + /// y = 4258727773875940690362607550498304598101071202821725296872974770776423442226 + /// The twisted Edwards form is + /// x = 995203441582195749578291179787384436505546430278305826713579947235728471134 + /// y = 5472060717959818805561601436314318772137091100104008585924551046643952123905 + const GENERATOR: EdwardsAffine = EdwardsAffine::new_unchecked(GENERATOR_X, GENERATOR_Y); + + type MontCurveConfig = EdwardsConfig; +} + +impl MontCurveConfig for EdwardsConfig { + /// COEFF_A = 168698 + const COEFF_A: Fq = MontFp!("168698"); + /// COEFF_B = 1 + const COEFF_B: Fq = Fq::ONE; + + type TECurveConfig = EdwardsConfig; +} + +/// GENERATOR_X = +/// 5299619240641551281634865583518297030282874472190772894086521144482721001553 +pub const GENERATOR_X: Fq = + MontFp!("5299619240641551281634865583518297030282874472190772894086521144482721001553"); + +/// GENERATOR_Y = +/// 16950150798460657717958625567821834550301663161624707787222815936182638968203 +pub const GENERATOR_Y: Fq = + MontFp!("16950150798460657717958625567821834550301663161624707787222815936182638968203"); diff --git a/curves/baby_jubjub/src/curves/tests.rs b/curves/baby_jubjub/src/curves/tests.rs new file mode 100644 index 000000000..7b1ea0955 --- /dev/null +++ b/curves/baby_jubjub/src/curves/tests.rs @@ -0,0 +1,4 @@ +use crate::*; +use ark_algebra_test_templates::*; + +test_group!(te; EdwardsProjective; te); diff --git a/curves/baby_jubjub/src/fields/fq.rs b/curves/baby_jubjub/src/fields/fq.rs new file mode 100644 index 000000000..0b7e3a5b6 --- /dev/null +++ b/curves/baby_jubjub/src/fields/fq.rs @@ -0,0 +1,8 @@ +use ark_ff::fields::{Fp256, MontBackend, MontConfig}; + +#[derive(MontConfig)] +#[modulus = "21888242871839275222246405745257275088548364400416034343698204186575808495617"] +#[generator = "5"] + +pub struct FqConfig; +pub type Fq = Fp256>; diff --git a/curves/baby_jubjub/src/fields/fr.rs b/curves/baby_jubjub/src/fields/fr.rs new file mode 100644 index 000000000..b69e3a9b4 --- /dev/null +++ b/curves/baby_jubjub/src/fields/fr.rs @@ -0,0 +1,8 @@ +use ark_ff::fields::{Fp256, MontBackend, MontConfig}; + +#[derive(MontConfig)] +#[modulus = "2736030358979909402780800718157159386076813972158567259200215660948447373041"] +#[generator = "31"] + +pub struct FrConfig; +pub type Fr = Fp256>; diff --git a/curves/baby_jubjub/src/fields/mod.rs b/curves/baby_jubjub/src/fields/mod.rs new file mode 100644 index 000000000..7afba5557 --- /dev/null +++ b/curves/baby_jubjub/src/fields/mod.rs @@ -0,0 +1,8 @@ +pub mod fq; +pub mod fr; + +pub use fq::*; +pub use fr::*; + +#[cfg(test)] +mod tests; diff --git a/curves/baby_jubjub/src/fields/tests.rs b/curves/baby_jubjub/src/fields/tests.rs new file mode 100644 index 000000000..299e84504 --- /dev/null +++ b/curves/baby_jubjub/src/fields/tests.rs @@ -0,0 +1,342 @@ +use ark_algebra_test_templates::*; +use ark_ff::{ + biginteger::BigInteger256 as BigInteger, + fields::{Field, LegendreSymbol::*}, + AdditiveGroup, One, Zero, +}; +use ark_std::str::FromStr; + +use crate::{Fq, Fr}; + +test_field!(fr; Fr; mont_prime_field); +test_field!(fq; Fq; mont_prime_field); + +#[test] +fn test_fq_add() { + let f1 = Fq::from_str( + "18386742314266644595564329008376577163854043021652781768352795308532764650733", + ) + .unwrap(); + let f2 = Fq::from_str( + "39786307610986038981023499868190793548353538256264351797285876981647142458383", + ) + .unwrap(); + let f3 = Fq::from_str( + "14396564181574133132095017386052820535110852477085064878242263917028290117882", + ) + .unwrap(); + assert!(!f1.is_zero()); + assert!(!f2.is_zero()); + assert!(!f3.is_zero()); + assert_eq!(f1 + &f2, f3); +} + +#[test] +fn test_fq_add_one() { + let f1 = Fq::from_str( + "4946875394261337176810256604189376311946643975348516311606738923340201185904", + ) + .unwrap(); + let f2 = Fq::from_str( + "4946875394261337176810256604189376311946643975348516311606738923340201185905", + ) + .unwrap(); + assert!(!f1.is_zero()); + assert!(!f2.is_zero()); + assert_eq!(f1 + &Fq::one(), f2); +} + +#[test] +fn test_fq_mul() { + let f1 = Fq::from_str( + "24703123148064348394273033316595937198355721297494556079070134653139656190956", + ) + .unwrap(); + let f2 = Fq::from_str( + "38196797080882758914424853878212529985425118523754343117256179679117054302131", + ) + .unwrap(); + let f3 = Fq::from_str( + "1321267396236123309645330145349353750536542060403774171357889269349508194307", + ) + .unwrap(); + assert!(!f1.is_zero()); + assert!(!f2.is_zero()); + assert!(!f3.is_zero()); + assert_eq!(f1 * &f2, f3); +} + +#[test] +fn test_fq_triple_mul() { + let f1 = Fq::from_str( + "23834398828139479510988224171342199299644042568628082836691700490363123893905", + ) + .unwrap(); + let f2 = Fq::from_str( + "48343809612844640454129919255697536258606705076971130519928764925719046689317", + ) + .unwrap(); + let f3 = Fq::from_str( + "22704845471524346880579660022678666462201713488283356385810726260959369106033", + ) + .unwrap(); + let f4 = Fq::from_str( + "7747776931431194635550680695131420638163057297019399136408144301550822179875", + ) + .unwrap(); + assert!(!f1.is_zero()); + assert!(!f2.is_zero()); + assert!(!f3.is_zero()); + assert_eq!(f1 * &f2 * &f3, f4); +} + +#[test] +fn test_fq_div() { + let f1 = Fq::from_str( + "31892744363926593013886463524057935370302352424137349660481695792871889573091", + ) + .unwrap(); + let f2 = Fq::from_str( + "47695868328933459965610498875668250916462767196500056002116961816137113470902", + ) + .unwrap(); + let f3 = Fq::from_str( + "7301086967624450577859019086314322648061398679982346993011603220910508457334", + ) + .unwrap(); + assert!(!f1.is_zero()); + assert!(!f2.is_zero()); + assert!(!f3.is_zero()); + assert_eq!(f1 / &f2, f3); +} + +#[test] +fn test_fq_sub() { + let f1 = Fq::from_str( + "18695869713129401390241150743745601908470616448391638969502807001833388904079", + ) + .unwrap(); + let f2 = Fq::from_str( + "10105476028534616828778879109836101003805485072436929139123765141153277007373", + ) + .unwrap(); + let f3 = Fq::from_str( + "8590393684594784561462271633909500904665131375954709830379041860680111896706", + ) + .unwrap(); + assert!(!f1.is_zero()); + assert!(!f2.is_zero()); + assert!(!f3.is_zero()); + assert_eq!(f1 - &f2, f3); +} + +#[test] +fn test_fq_double_in_place_thrice() { + let mut f1 = Fq::from_str( + "32768907806651393940832831055386272949401004221411141755415956893066040832473", + ) + .unwrap(); + let f3 = Fq::from_str( + "21380590862979124081952185245260157621176025366712756262647409092194433207997", + ) + .unwrap(); + assert!(!f1.is_zero()); + assert!(!f3.is_zero()); + f1.double_in_place(); + f1.double_in_place(); + f1.double_in_place(); + assert_eq!(f1, f3); +} + +#[test] +fn test_fq_generate_random_ed_on_bn254_point() { + let a = Fq::from_str("168700").unwrap(); + + let d = Fq::from_str("168696").unwrap(); + let y = Fq::from_str( + "19987327827845206670850937090314462639017692512983955920885166014935289314257", + ) + .unwrap(); + let x2 = Fq::from_str( + "2144239075372598103060889495211040948751593385312551803225522963913923559328", + ) + .unwrap(); + + let computed_y2 = y.square(); + let y2 = Fq::from_str( + "11134206686211572308995578277928848431421308813024790181507137950838333998633", + ) + .unwrap(); + assert_eq!(y2, computed_y2); + + let computed_dy2 = d * &computed_y2; + let dy2 = + Fq::from_str("345576003677591687256955722467813448317229128849323754147891993737799010947") + .unwrap(); + assert_eq!(dy2, computed_dy2); + + let computed_divisor = computed_dy2 - a; + let divisor = + Fq::from_str("345576003677591687256955722467813448317229128849323754147891993737798842247") + .unwrap(); + assert_eq!(divisor, computed_divisor); + + let computed_x2 = (computed_y2 - &Fq::one()) / &computed_divisor; + assert_eq!(x2, computed_x2); + + let x = Fq::from_str( + "4801447892755635304907919953550459075619191823587157449340656925102682829025", + ) + .unwrap(); + let computed_x = computed_x2.sqrt().unwrap(); + assert_eq!(computed_x.square(), x2); + assert_eq!(x, computed_x); + + fn add<'a>(curr: (Fq, Fq), other: &'a (Fq, Fq)) -> (Fq, Fq) { + let y1y2 = curr.1 * &other.1; + let x1x2 = curr.0 * &other.0; + let a = Fq::from_str("168700").unwrap(); + let d = Fq::from_str("168696").unwrap(); + let dx1x2y1y2 = d * &y1y2 * &x1x2; + + let d1 = Fq::one() + &dx1x2y1y2; + let d2 = Fq::one() - &dx1x2y1y2; + + let x1y2 = curr.0 * &other.1; + let y1x2 = curr.1 * &other.0; + + let x = (x1y2 + &y1x2) / &d1; + let y = (y1y2 - a * &x1x2) / &d2; + + (x, y) + } + + let result = add((x, y), &(x, y)); + let result = add(result, &result); + let result = add(result, &result); + + let point_x = + Fq::from_str("380676173762867192861894055350059333852732198308367125138259398265363727587") + .unwrap(); + let point_y = Fq::from_str( + "8435074244857818446059206728316702149733931432112984450960434710303841866985", + ) + .unwrap(); + assert_eq!((point_x, point_y), result); +} + +#[test] +fn test_fq_square_in_place() { + let mut f1 = Fq::from_str( + "6060110850233386730847324622937480088943976359504617699731744947670229990461", + ) + .unwrap(); + let f3 = Fq::from_str( + "17018926051730832095053393285350575966874590491719897015583930476179087429684", + ) + .unwrap(); + assert!(!f1.is_zero()); + assert!(!f3.is_zero()); + f1.square_in_place(); + assert_eq!(f1, f3); +} + +#[test] +fn test_fq_from_str() { + let f1_from_repr = Fq::from(BigInteger::new([ + 0xab8a2535947d1a77, + 0x9ba74cbfda0bbcda, + 0xe928b59724d60baf, + 0x1cccaaeb9bb1680a, + ])); + let f1 = Fq::from_str( + "13026376210409056429264774981357153555336288129100724591327877625017068755575", + ) + .unwrap(); + let f2_from_repr = Fq::from(BigInteger::new([ + 0x97e9103775d2f35c, + 0xbe6756b6c587544b, + 0x6ee38c3afd88ef4b, + 0x2bacd150f540c677, + ])); + let f2 = Fq::from_str( + "19754794831832707859764530223239420866832328728734160755396495950822165902172", + ) + .unwrap(); + assert_eq!(f1_from_repr, f1); + assert_eq!(f2_from_repr, f2); +} + +#[test] +fn test_fq_legendre() { + assert_eq!(QuadraticResidue, Fq::one().legendre()); + assert_eq!(Zero, Fq::zero().legendre()); + + let e = BigInteger::new([ + 0x2e8de1a676c03be8, + 0x73350d34fe25a560, + 0x7ea085919029688e, + 0x1d0868cb993cf28, + ]); + assert_eq!(QuadraticResidue, Fq::from(e).legendre()); + let e = BigInteger::new([ + 0x891d8cc23c8d0706, + 0xe91800e007db2698, + 0xfff380321e9ac7a7, + 0x2659e28bd17eab6, + ]); + assert_eq!(QuadraticNonResidue, Fq::from(e).legendre()); +} + +#[test] +fn test_fr_add() { + let f1 = Fr::from(BigInteger::new([ + 0xccfc9a195e0f5c46, + 0xaed4874d13fb1285, + 0x27368f86ca2848eb, + 0x4f8adcfeb44fccc, + ])); + let f2 = Fr::from(BigInteger::new([ + 0x661ff05bf8570851, + 0x1b171f4c59be97ef, + 0x5d2ce7f9b4d701f3, + 0x1e0e794623e0f68, + ])); + let f3 = Fr::from(BigInteger::new([ + 0xcba9f2991d453da6, + 0x1eacb8e13498bc6a, + 0x4d596ec9aecf1fd3, + 0xcd0b95f15cd82f, + ])); + assert_eq!(f1 + &f2, f3); +} + +#[test] +fn test_fr_mul() { + let f1 = Fr::from(BigInteger::new([ + 0xc2964d2dd5fb980f, + 0xbab64d599c57e496, + 0x39cae13e7d1d4f78, + 0x1aa995aa4de205c, + ])); + let f2 = Fr::from(BigInteger::new([ + 0xc256e720cd43533b, + 0x3bfbadf6247e13bb, + 0x94c3d63a53714f63, + 0x10f8a7bf74efd57, + ])); + let f3 = Fr::from(BigInteger::new([ + 0x5eac88be41e0e1fd, + 0x57aab36675b11e24, + 0x835582d896b4d13f, + 0x4808736e213036e, + ])); + assert_eq!(f1 * &f2, f3); +} + +#[test] +fn test_fr_from_str() { + let f100_from_repr = Fr::from(BigInteger::new([0x64, 0, 0, 0])); + let f100 = Fr::from_str("100").unwrap(); + assert_eq!(f100_from_repr, f100); +} diff --git a/curves/baby_jubjub/src/lib.rs b/curves/baby_jubjub/src/lib.rs new file mode 100644 index 000000000..7a8b89880 --- /dev/null +++ b/curves/baby_jubjub/src/lib.rs @@ -0,0 +1,28 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![deny( + warnings, + unused, + future_incompatible, + nonstandard_style, + rust_2018_idioms +)] +#![forbid(unsafe_code)] + +//! This library implements the Baby Jubjub Twisted Edwards curve compatible with ERC-2494 +//! +//! Curve information: +//! * Base field: q = +//! 21888242871839275222246405745257275088548364400416034343698204186575808495617 +//! * Scalar field: r = +//! 2736030358979909402780800718157159386076813972158567259200215660948447373041 +//! * Curve equation: Ax^2 + y^2 = 1 + Dx^2y^2, where +//! * A = 168700 +//! * D = 168696 + +#[cfg(feature = "r1cs")] +pub mod constraints; +mod curves; +mod fields; + +pub use curves::*; +pub use fields::*;