diff --git a/CHANGELOG.md b/CHANGELOG.md index 9863d4a8..fa5c9406 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ `ark-mnt6-753`. - [\#7](https://github.com/arkworks-rs/curves/pull/7) Add benchmarks for Edwards curves. - [\#19](https://github.com/arkworks-rs/curves/pull/19) Change field constants to be provided as normal strings, instead of in Montgomery form. +- [\#53](https://github.com/arkworks-rs/curves/pull/53) Add benchmarks for Pallas and Vesta curves. +- [\#54](https://github.com/arkworks-rs/curves/pull/54) Add support for the Pluto-Eris half-pairing cycle. ### Improvements - [\#42](https://github.com/arkworks-rs/curves/pull/42) Remove the dependency of `rand_xorshift`. @@ -30,4 +32,4 @@ ## v0.1.0 -Initial Release \ No newline at end of file +Initial Release diff --git a/Cargo.toml b/Cargo.toml index e3c83d63..7b37eab8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,9 @@ members = [ "pallas", "vesta", + + "pluto", + "eris", ] [profile.release] diff --git a/README.md b/README.md index 4f760ba9..a2f15b7c 100644 --- a/README.md +++ b/README.md @@ -31,5 +31,9 @@ This repository contains implementations of some popular elliptic curves. The cu * [`ark-ed-on-mnt4-753`](ed_on_mnt4_753): Implements a Twisted Edwards curve atop the scalar field of MNT4-753 ### [Pasta](https://electriccoin.co/blog/the-pasta-curves-for-halo-2-and-beyond/) cycle of curves -* [`ark-pallas`](pallas): Implements Pallas, a prime-order curve that forms an amicable pair with Vesta -* [`ark-vesta`](vesta): Implements Vesta, a prime-order curve that forms an amicable pair with Pallas +* [`ark-pallas`](pallas): Implements Pallas, a 255-bit prime-order curve that forms an amicable pair with Vesta +* [`ark-vesta`](vesta): Implements Vesta, a 255-bit prime-order curve that forms an amicable pair with Pallas + +### [Pluto-Eris](https://github.com/daira/pluto-eris/) half-pairing cycle of curves +* [`ark-pluto`](pluto): Implements Pluto, a 446-bit prime-order pairing-friendly curve that forms an amicable pair with Eris +* [`ark-eris`](eris): Implements Eris, a 446-bit prime-order curve that forms an amicable pair with Pluto diff --git a/curve-benches/Cargo.toml b/curve-benches/Cargo.toml index 61e962ad..c063d9ba 100644 --- a/curve-benches/Cargo.toml +++ b/curve-benches/Cargo.toml @@ -7,7 +7,8 @@ authors = [ "Matthew Green", "Ian Miers", "Pratyush Mishra", - "Howard Wu" + "Howard Wu", + "Daira Hopwood" ] description = "A benchmark library for finite fields and elliptic curves" homepage = "https://arkworks.rs" @@ -42,6 +43,10 @@ ark-bls12-381 = { path = "../bls12_381" } ark-ed-on-bls12-381 = { path = "../ed_on_bls12_381" } ark-bw6-761 = { path = "../bw6_761" } ark-cp6-782 = { path = "../cp6_782" } +ark-pallas = { path = "../pallas" } +ark-vesta = { path = "../vesta" } +ark-pluto = { path = "../pluto" } +ark-eris = { path = "../eris" } [features] asm = [ "ark-ff/asm"] @@ -100,3 +105,23 @@ harness = false name = "mnt6_753" path = "benches/mnt6_753.rs" harness = false + +[[bench]] +name = "pallas" +path = "benches/pallas.rs" +harness = false + +[[bench]] +name = "vesta" +path = "benches/vesta.rs" +harness = false + +[[bench]] +name = "pluto" +path = "benches/pluto.rs" +harness = false + +[[bench]] +name = "eris" +path = "benches/eris.rs" +harness = false diff --git a/curve-benches/benches/eris.rs b/curve-benches/benches/eris.rs new file mode 100644 index 00000000..1aec2fa0 --- /dev/null +++ b/curve-benches/benches/eris.rs @@ -0,0 +1,19 @@ +use ark_curve_benches::*; +use ark_std::ops::{AddAssign, MulAssign, SubAssign}; + +use ark_ec::ProjectiveCurve; +use ark_eris::{fq::Fq, fr::Fr, Affine as GAffine, Projective as G}; +use ark_ff::{ + biginteger::{BigInteger448 as FrRepr, BigInteger448 as FqRepr}, + BigInteger, Field, PrimeField, SquareRootField, UniformRand, +}; + +mod g { + use super::*; + ec_bench!(G, GAffine); +} + +f_bench!(Fq, Fq, FqRepr, FqRepr, fq); +f_bench!(Fr, Fr, FrRepr, FrRepr, fr); + +bencher::benchmark_main!(fq, fr, g::group_ops); diff --git a/curve-benches/benches/pallas.rs b/curve-benches/benches/pallas.rs new file mode 100644 index 00000000..adbb392c --- /dev/null +++ b/curve-benches/benches/pallas.rs @@ -0,0 +1,19 @@ +use ark_curve_benches::*; +use ark_std::ops::{AddAssign, MulAssign, SubAssign}; + +use ark_ec::ProjectiveCurve; +use ark_ff::{ + biginteger::{BigInteger256 as FrRepr, BigInteger256 as FqRepr}, + BigInteger, Field, PrimeField, SquareRootField, UniformRand, +}; +use ark_pallas::{fq::Fq, fr::Fr, Affine as GAffine, Projective as G}; + +mod g { + use super::*; + ec_bench!(G, GAffine); +} + +f_bench!(Fq, Fq, FqRepr, FqRepr, fq); +f_bench!(Fr, Fr, FrRepr, FrRepr, fr); + +bencher::benchmark_main!(fq, fr, g::group_ops); diff --git a/curve-benches/benches/pluto.rs b/curve-benches/benches/pluto.rs new file mode 100644 index 00000000..ce92a831 --- /dev/null +++ b/curve-benches/benches/pluto.rs @@ -0,0 +1,19 @@ +use ark_curve_benches::*; +use ark_std::ops::{AddAssign, MulAssign, SubAssign}; + +use ark_ec::ProjectiveCurve; +use ark_ff::{ + biginteger::{BigInteger448 as FrRepr, BigInteger448 as FqRepr}, + BigInteger, Field, PrimeField, SquareRootField, UniformRand, +}; +use ark_pluto::{fq::Fq, fr::Fr, Affine as GAffine, Projective as G}; + +mod g { + use super::*; + ec_bench!(G, GAffine); +} + +f_bench!(Fq, Fq, FqRepr, FqRepr, fq); +f_bench!(Fr, Fr, FrRepr, FrRepr, fr); + +bencher::benchmark_main!(fq, fr, g::group_ops); diff --git a/curve-benches/benches/vesta.rs b/curve-benches/benches/vesta.rs new file mode 100644 index 00000000..11a0fd4a --- /dev/null +++ b/curve-benches/benches/vesta.rs @@ -0,0 +1,19 @@ +use ark_curve_benches::*; +use ark_std::ops::{AddAssign, MulAssign, SubAssign}; + +use ark_ec::ProjectiveCurve; +use ark_ff::{ + biginteger::{BigInteger256 as FrRepr, BigInteger256 as FqRepr}, + BigInteger, Field, PrimeField, SquareRootField, UniformRand, +}; +use ark_vesta::{fq::Fq, fr::Fr, Affine as GAffine, Projective as G}; + +mod g { + use super::*; + ec_bench!(G, GAffine); +} + +f_bench!(Fq, Fq, FqRepr, FqRepr, fq); +f_bench!(Fr, Fr, FrRepr, FrRepr, fr); + +bencher::benchmark_main!(fq, fr, g::group_ops); diff --git a/eris/Cargo.toml b/eris/Cargo.toml new file mode 100644 index 00000000..10e15ad6 --- /dev/null +++ b/eris/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "ark-eris" +version = "0.2.0" +authors = [ "Daira Hopwood", "arkworks contributors" ] +description = "The Eris prime-order elliptic curve" +homepage = "https://arkworks.rs" +repository = "https://github.com/arkworks-rs/curves" +documentation = "https://docs.rs/ark-eris/" +keywords = ["cryptography", "finite-fields", "elliptic-curves" ] +categories = ["cryptography"] +include = ["Cargo.toml", "src"] +license = "MIT/Apache-2.0" +edition = "2018" + +[dependencies] +ark-ff = { version = "^0.2.0", default-features = false } +ark-ec = { version = "^0.2.0", default-features = false } +ark-r1cs-std = { version = "^0.2.0", default-features = false, optional = true } +ark-std = { version = "^0.2.0", default-features = false } +ark-pluto = { version = "^0.2.0", path = "../pluto", default-features = false, features = [ "scalar_field", "base_field" ] } + +[dev-dependencies] +ark-relations = { version = "^0.2.0", default-features = false } +ark-serialize = { version = "^0.2.0", default-features = false } +ark-algebra-test-templates = { version = "^0.2.0", default-features = false } +ark-curve-constraint-tests = { path = "../curve-constraint-tests", default-features = false } + +[features] +default = [] +std = [ "ark-std/std", "ark-ff/std", "ark-ec/std" ] +r1cs = [ "ark-r1cs-std" ] diff --git a/eris/src/constraints/curves.rs b/eris/src/constraints/curves.rs new file mode 100644 index 00000000..0ef788ba --- /dev/null +++ b/eris/src/constraints/curves.rs @@ -0,0 +1,12 @@ +use crate::*; +use ark_r1cs_std::groups::curves::short_weierstrass::ProjectiveVar; + +use crate::constraints::FBaseVar; + +/// A group element in the Eris prime-order group. +pub type GVar = ProjectiveVar; + +#[test] +fn test() { + ark_curve_constraint_tests::curves::sw_test::().unwrap(); +} diff --git a/eris/src/constraints/fields.rs b/eris/src/constraints/fields.rs new file mode 100644 index 00000000..e2f08b05 --- /dev/null +++ b/eris/src/constraints/fields.rs @@ -0,0 +1,10 @@ +use crate::fq::Fq; +use ark_r1cs_std::fields::fp::FpVar; + +/// A variable that is the R1CS equivalent of `crate::Fq`. +pub type FBaseVar = FpVar; + +#[test] +fn test() { + ark_curve_constraint_tests::fields::field_test::<_, _, FBaseVar>().unwrap(); +} diff --git a/eris/src/constraints/mod.rs b/eris/src/constraints/mod.rs new file mode 100644 index 00000000..174db04c --- /dev/null +++ b/eris/src/constraints/mod.rs @@ -0,0 +1,107 @@ +//! This module implements the R1CS equivalent of `ark_eris`. +//! +//! It implements field variables for `crate::Fq`, +//! and group variables for `crate::GroupProjective`. +//! +//! The field underlying these constraints is `crate::Fq`. +//! +//! # Examples +//! +//! One can perform standard algebraic operations on `FBaseVar`: +//! +//! ``` +//! # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { +//! use ark_std::UniformRand; +//! use ark_relations::r1cs::*; +//! use ark_r1cs_std::prelude::*; +//! use ark_eris::{*, constraints::*}; +//! +//! let cs = ConstraintSystem::::new_ref(); +//! // This rng is just for test purposes; do not use it +//! // in real applications. +//! let mut rng = ark_std::test_rng(); +//! +//! // Generate some random `Fq` elements. +//! let a_native = Fq::rand(&mut rng); +//! let b_native = Fq::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = FBaseVar::new_witness(ark_relations::ns!(cs, "generate_a"), || Ok(a_native))?; +//! let b = FBaseVar::new_witness(ark_relations::ns!(cs, "generate_b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = FBaseVar::new_constant(ark_relations::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = FBaseVar::new_constant(ark_relations::ns!(cs, "b_as_constant"), b_native)?; +//! +//! let one = FBaseVar::one(); +//! let zero = FBaseVar::zero(); +//! +//! // Sanity check one + one = two +//! let two = &one + &one + &zero; +//! two.enforce_equal(&one.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that the value of &a * &b is correct. +//! assert_eq!((&a * &b).value()?, a_native * &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` +//! +//! One can also perform standard algebraic operations on `GVar`: +//! +//! ``` +//! # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { +//! # use ark_std::UniformRand; +//! # use ark_relations::r1cs::*; +//! # use ark_r1cs_std::prelude::*; +//! # use ark_eris::{*, constraints::*}; +//! +//! # let cs = ConstraintSystem::::new_ref(); +//! # let mut rng = ark_std::test_rng(); +//! +//! // Generate some random `Projective` elements. +//! let a_native = Projective::rand(&mut rng); +//! let b_native = Projective::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = GVar::new_witness(ark_relations::ns!(cs, "a"), || Ok(a_native))?; +//! let b = GVar::new_witness(ark_relations::ns!(cs, "b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = GVar::new_constant(ark_relations::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = GVar::new_constant(ark_relations::ns!(cs, "b_as_constant"), b_native)?; +//! +//! // This returns the identity. +//! let zero = GVar::zero(); +//! +//! // Sanity check one + one = two +//! let two_a = &a + &a + &zero; +//! two_a.enforce_equal(&a.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` + +mod curves; +mod fields; + +pub use curves::*; +pub use fields::*; diff --git a/eris/src/curves/mod.rs b/eris/src/curves/mod.rs new file mode 100644 index 00000000..9c4a496f --- /dev/null +++ b/eris/src/curves/mod.rs @@ -0,0 +1,51 @@ +use crate::{fq::Fq, fr::Fr}; +use ark_ec::{ + models::{ModelParameters, SWModelParameters}, + short_weierstrass_jacobian::{GroupAffine, GroupProjective}, +}; +use ark_ff::{field_new, Zero}; + +#[cfg(test)] +mod tests; + +#[derive(Copy, Clone, Default, PartialEq, Eq)] +pub struct ErisParameters; + +impl ModelParameters for ErisParameters { + type BaseField = Fq; + type ScalarField = Fr; +} + +pub type Affine = GroupAffine; +pub type Projective = GroupProjective; + +impl SWModelParameters for ErisParameters { + /// COEFF_A = 0 + const COEFF_A: Fq = field_new!(Fq, "0"); + + /// COEFF_B = 57 + const COEFF_B: Fq = field_new!(Fq, "57"); + + /// COFACTOR = 1 + const COFACTOR: &'static [u64] = &[0x1]; + + /// COFACTOR_INV = 1 + const COFACTOR_INV: Fr = field_new!(Fr, "1"); + + /// AFFINE_GENERATOR_COEFFS = (G1_GENERATOR_X, G1_GENERATOR_Y) + const AFFINE_GENERATOR_COEFFS: (Self::BaseField, Self::BaseField) = + (G_GENERATOR_X, G_GENERATOR_Y); + + #[inline(always)] + fn mul_by_a(_: &Self::BaseField) -> Self::BaseField { + Self::BaseField::zero() + } +} + +/// G_GENERATOR_X = -2 +/// Encoded in Montgomery form, so the value here is -R mod p. +pub const G_GENERATOR_X: Fq = field_new!(Fq, "-2"); + +/// G_GENERATOR_Y = 7 +/// Encoded in Montgomery form, so the value here is 2R mod p. +pub const G_GENERATOR_Y: Fq = field_new!(Fq, "7"); diff --git a/eris/src/curves/tests.rs b/eris/src/curves/tests.rs new file mode 100644 index 00000000..42b6a580 --- /dev/null +++ b/eris/src/curves/tests.rs @@ -0,0 +1,39 @@ +#![allow(unused_imports)] +use ark_ff::{ + fields::{Field, FpParameters, PrimeField, SquareRootField}, + One, Zero, +}; +use ark_serialize::CanonicalSerialize; +use ark_std::test_rng; + +use ark_ec::{models::SWModelParameters, AffineCurve, PairingEngine, ProjectiveCurve}; +use ark_std::ops::{AddAssign, MulAssign}; +use ark_std::rand::Rng; + +use crate::{Affine, ErisParameters, Projective}; + +use ark_algebra_test_templates::{ + curves::{curve_tests, sw_tests}, + groups::group_test, +}; + +#[test] +fn test_projective_curve() { + curve_tests::(); + sw_tests::(); +} + +#[test] +fn test_projective_group() { + let mut rng = test_rng(); + let a: Projective = rng.gen(); + let b: Projective = rng.gen(); + group_test(a, b); +} + +#[test] +fn test_generator() { + let generator = Affine::prime_subgroup_generator(); + assert!(generator.is_on_curve()); + assert!(generator.is_in_correct_subgroup_assuming_on_curve()); +} diff --git a/eris/src/fields/fq.rs b/eris/src/fields/fq.rs new file mode 100644 index 00000000..55435f23 --- /dev/null +++ b/eris/src/fields/fq.rs @@ -0,0 +1 @@ +pub use ark_pluto::{Fr as Fq, FrParameters as FqParameters}; diff --git a/eris/src/fields/fr.rs b/eris/src/fields/fr.rs new file mode 100644 index 00000000..b093b7d6 --- /dev/null +++ b/eris/src/fields/fr.rs @@ -0,0 +1 @@ +pub use ark_pluto::{Fq as Fr, FqParameters as FrParameters}; diff --git a/eris/src/fields/mod.rs b/eris/src/fields/mod.rs new file mode 100644 index 00000000..5156179a --- /dev/null +++ b/eris/src/fields/mod.rs @@ -0,0 +1,8 @@ +pub mod fq; +pub use self::fq::*; + +pub mod fr; +pub use self::fr::*; + +#[cfg(test)] +mod tests; diff --git a/eris/src/fields/tests.rs b/eris/src/fields/tests.rs new file mode 100644 index 00000000..26807e23 --- /dev/null +++ b/eris/src/fields/tests.rs @@ -0,0 +1,26 @@ +use ark_std::rand::Rng; +use ark_std::test_rng; + +use crate::*; + +use ark_algebra_test_templates::fields::*; + +#[test] +fn test_fr() { + let mut rng = test_rng(); + let a: Fr = rng.gen(); + let b: Fr = rng.gen(); + field_test(a, b); + sqrt_field_test(a); + primefield_test::(); +} + +#[test] +fn test_fq() { + let mut rng = test_rng(); + let a: Fq = rng.gen(); + let b: Fq = rng.gen(); + field_test(a, b); + sqrt_field_test(a); + primefield_test::(); +} diff --git a/eris/src/lib.rs b/eris/src/lib.rs new file mode 100644 index 00000000..14c3f855 --- /dev/null +++ b/eris/src/lib.rs @@ -0,0 +1,32 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![deny( + warnings, + unused, + future_incompatible, + nonstandard_style, + rust_2018_idioms +)] +#![forbid(unsafe_code)] + +//! This library implements the prime-order curve Eris, generated by +//! [Daira Hopwood](https://github.com/daira/pluto-eris). This curve is part +//! of a half-pairing cycle with Pluto, i.e. its scalar field and base field +//! respectively are the base field and scalar field of Pluto. +//! +//! +//! Curve information: +//! * Base field: q = +//! 0x24000000000024000130e0000d7f70e4a803ca76f439266f443f9a5c7a8a6c7be4a775fe8e177fd69ca7e85d60050af41ffffcd300000001 +//! * Scalar field: r = +//! 0x24000000000024000130e0000d7f70e4a803ca76f439266f443f9a5cda8a6c7be4a7a5fe8fadffd6a2a7e8c30006b9459ffffcd300000001 +//! * Curve equation: y^2 = x^3 + 57 +//! * Valuation(q - 1, 2) = 32 +//! * Valuation(r - 1, 2) = 32 + +#[cfg(feature = "r1cs")] +pub mod constraints; +mod curves; +mod fields; + +pub use curves::*; +pub use fields::*; diff --git a/pluto/Cargo.toml b/pluto/Cargo.toml new file mode 100644 index 00000000..3ebbcfa1 --- /dev/null +++ b/pluto/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "ark-pluto" +version = "0.2.0" +authors = [ "Daira Hopwood", "arkworks contributors" ] +description = "The Pluto prime-order pairing-friendly elliptic curve" +homepage = "https://arkworks.rs" +repository = "https://github.com/arkworks-rs/curves" +documentation = "https://docs.rs/ark-pluto/" +keywords = ["cryptography", "finite-fields", "elliptic-curves" ] +categories = ["cryptography"] +include = ["Cargo.toml", "src"] +license = "MIT/Apache-2.0" +edition = "2018" + +[dependencies] +ark-ff = { version = "^0.2.0", default-features = false } +ark-ec = { version = "^0.2.0", default-features = false } +ark-r1cs-std = { version = "^0.2.0", default-features = false, optional = true } +ark-std = { version = "^0.2.0", default-features = false } + +[dev-dependencies] +ark-relations = { version = "^0.2.0", default-features = false } +ark-serialize = { version = "^0.2.0", default-features = false } +ark-algebra-test-templates = { version = "^0.2.0", default-features = false } +ark-curve-constraint-tests = { path = "../curve-constraint-tests", default-features = false } + +[features] +default = [ "curve" ] +std = [ "ark-std/std", "ark-ff/std", "ark-ec/std" ] + +curve = [ "scalar_field", "base_field" ] +scalar_field = [] +base_field = [] +r1cs = [ "base_field", "ark-r1cs-std" ] diff --git a/pluto/src/constraints/curves.rs b/pluto/src/constraints/curves.rs new file mode 100644 index 00000000..f9b1cb5c --- /dev/null +++ b/pluto/src/constraints/curves.rs @@ -0,0 +1,12 @@ +use crate::*; +use ark_r1cs_std::groups::curves::short_weierstrass::ProjectiveVar; + +use crate::constraints::FBaseVar; + +/// A group element in the Pluto prime-order group. +pub type GVar = ProjectiveVar; + +#[test] +fn test() { + ark_curve_constraint_tests::curves::sw_test::().unwrap(); +} diff --git a/pluto/src/constraints/fields.rs b/pluto/src/constraints/fields.rs new file mode 100644 index 00000000..e2f08b05 --- /dev/null +++ b/pluto/src/constraints/fields.rs @@ -0,0 +1,10 @@ +use crate::fq::Fq; +use ark_r1cs_std::fields::fp::FpVar; + +/// A variable that is the R1CS equivalent of `crate::Fq`. +pub type FBaseVar = FpVar; + +#[test] +fn test() { + ark_curve_constraint_tests::fields::field_test::<_, _, FBaseVar>().unwrap(); +} diff --git a/pluto/src/constraints/mod.rs b/pluto/src/constraints/mod.rs new file mode 100644 index 00000000..6cabc1a6 --- /dev/null +++ b/pluto/src/constraints/mod.rs @@ -0,0 +1,107 @@ +//! This module implements the R1CS equivalent of `ark_pluto`. +//! +//! It implements field variables for `crate::Fq`, +//! and group variables for `crate::GroupProjective`. +//! +//! The field underlying these constraints is `crate::Fq`. +//! +//! # Examples +//! +//! One can perform standard algebraic operations on `FBaseVar`: +//! +//! ``` +//! # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { +//! use ark_std::UniformRand; +//! use ark_relations::r1cs::*; +//! use ark_r1cs_std::prelude::*; +//! use ark_pluto::{*, constraints::*}; +//! +//! let cs = ConstraintSystem::::new_ref(); +//! // This rng is just for test purposes; do not use it +//! // in real applications. +//! let mut rng = ark_std::test_rng(); +//! +//! // Generate some random `Fq` elements. +//! let a_native = Fq::rand(&mut rng); +//! let b_native = Fq::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = FBaseVar::new_witness(ark_relations::ns!(cs, "generate_a"), || Ok(a_native))?; +//! let b = FBaseVar::new_witness(ark_relations::ns!(cs, "generate_b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = FBaseVar::new_constant(ark_relations::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = FBaseVar::new_constant(ark_relations::ns!(cs, "b_as_constant"), b_native)?; +//! +//! let one = FBaseVar::one(); +//! let zero = FBaseVar::zero(); +//! +//! // Sanity check one + one = two +//! let two = &one + &one + &zero; +//! two.enforce_equal(&one.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that the value of &a * &b is correct. +//! assert_eq!((&a * &b).value()?, a_native * &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` +//! +//! One can also perform standard algebraic operations on `GVar`: +//! +//! ``` +//! # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { +//! # use ark_std::UniformRand; +//! # use ark_relations::r1cs::*; +//! # use ark_r1cs_std::prelude::*; +//! # use ark_pluto::{*, constraints::*}; +//! +//! # let cs = ConstraintSystem::::new_ref(); +//! # let mut rng = ark_std::test_rng(); +//! +//! // Generate some random `Projective` elements. +//! let a_native = Projective::rand(&mut rng); +//! let b_native = Projective::rand(&mut rng); +//! +//! // Allocate `a_native` and `b_native` as witness variables in `cs`. +//! let a = GVar::new_witness(ark_relations::ns!(cs, "a"), || Ok(a_native))?; +//! let b = GVar::new_witness(ark_relations::ns!(cs, "b"), || Ok(b_native))?; +//! +//! // Allocate `a_native` and `b_native` as constants in `cs`. This does not add any +//! // constraints or variables. +//! let a_const = GVar::new_constant(ark_relations::ns!(cs, "a_as_constant"), a_native)?; +//! let b_const = GVar::new_constant(ark_relations::ns!(cs, "b_as_constant"), b_native)?; +//! +//! // This returns the identity. +//! let zero = GVar::zero(); +//! +//! // Sanity check one + one = two +//! let two_a = &a + &a + &zero; +//! two_a.enforce_equal(&a.double()?)?; +//! +//! assert!(cs.is_satisfied()?); +//! +//! // Check that the value of &a + &b is correct. +//! assert_eq!((&a + &b).value()?, a_native + &b_native); +//! +//! // Check that operations on variables and constants are equivalent. +//! (&a + &b).enforce_equal(&(&a_const + &b_const))?; +//! assert!(cs.is_satisfied()?); +//! # Ok(()) +//! # } +//! ``` + +mod curves; +mod fields; + +pub use curves::*; +pub use fields::*; diff --git a/pluto/src/curves/mod.rs b/pluto/src/curves/mod.rs new file mode 100644 index 00000000..4af540f2 --- /dev/null +++ b/pluto/src/curves/mod.rs @@ -0,0 +1,49 @@ +use crate::{fq::Fq, fr::Fr}; +use ark_ec::{ + models::{ModelParameters, SWModelParameters}, + short_weierstrass_jacobian::{GroupAffine, GroupProjective}, +}; +use ark_ff::{field_new, Zero}; + +#[cfg(test)] +mod tests; + +#[derive(Copy, Clone, Default, PartialEq, Eq)] +pub struct PlutoParameters; + +impl ModelParameters for PlutoParameters { + type BaseField = Fq; + type ScalarField = Fr; +} + +pub type Affine = GroupAffine; +pub type Projective = GroupProjective; + +impl SWModelParameters for PlutoParameters { + /// COEFF_A = 0 + const COEFF_A: Fq = field_new!(Fq, "0"); + + /// COEFF_B = 57 + const COEFF_B: Fq = field_new!(Fq, "57"); + + /// COFACTOR = 1 + const COFACTOR: &'static [u64] = &[0x1]; + + /// COFACTOR_INV = 1 + const COFACTOR_INV: Fr = field_new!(Fr, "1"); + + /// AFFINE_GENERATOR_COEFFS = (G1_GENERATOR_X, G1_GENERATOR_Y) + const AFFINE_GENERATOR_COEFFS: (Self::BaseField, Self::BaseField) = + (G_GENERATOR_X, G_GENERATOR_Y); + + #[inline(always)] + fn mul_by_a(_: &Self::BaseField) -> Self::BaseField { + Self::BaseField::zero() + } +} + +/// G_GENERATOR_X = -2 +pub const G_GENERATOR_X: Fq = field_new!(Fq, "-2"); + +/// G_GENERATOR_Y = 7 +pub const G_GENERATOR_Y: Fq = field_new!(Fq, "7"); diff --git a/pluto/src/curves/tests.rs b/pluto/src/curves/tests.rs new file mode 100644 index 00000000..e3e3b335 --- /dev/null +++ b/pluto/src/curves/tests.rs @@ -0,0 +1,39 @@ +#![allow(unused_imports)] +use ark_ff::{ + fields::{Field, FpParameters, PrimeField, SquareRootField}, + One, Zero, +}; +use ark_serialize::CanonicalSerialize; +use ark_std::test_rng; + +use ark_ec::{models::SWModelParameters, AffineCurve, PairingEngine, ProjectiveCurve}; +use ark_std::ops::{AddAssign, MulAssign}; +use ark_std::rand::Rng; + +use crate::{Affine, PlutoParameters, Projective}; + +use ark_algebra_test_templates::{ + curves::{curve_tests, sw_tests}, + groups::group_test, +}; + +#[test] +fn test_projective_curve() { + curve_tests::(); + sw_tests::(); +} + +#[test] +fn test_projective_group() { + let mut rng = test_rng(); + let a: Projective = rng.gen(); + let b: Projective = rng.gen(); + group_test(a, b); +} + +#[test] +fn test_generator() { + let generator = Affine::prime_subgroup_generator(); + assert!(generator.is_on_curve()); + assert!(generator.is_in_correct_subgroup_assuming_on_curve()); +} diff --git a/pluto/src/fields/fq.rs b/pluto/src/fields/fq.rs new file mode 100644 index 00000000..839e8c8e --- /dev/null +++ b/pluto/src/fields/fq.rs @@ -0,0 +1,115 @@ +use ark_ff::{ + biginteger::BigInteger448 as BigInteger, + fields::{FftParameters, Fp448, Fp448Parameters}, +}; + +pub type Fq = Fp448; + +pub struct FqParameters; + +impl Fp448Parameters for FqParameters {} +impl FftParameters for FqParameters { + type BigInt = BigInteger; + + const TWO_ADICITY: u32 = 32; + + // TWO_ADIC_ROOT_OF_UNITY = GENERATOR^T + // Encoded in Montgomery form, so the value here is (19^T)R mod q. + const TWO_ADIC_ROOT_OF_UNITY: BigInteger = BigInteger([ + 0xebda7b4bdd4ba274, + 0x2e31416b79bde3fd, + 0x86c37b96cdca3d4b, + 0x9c352819e72e0970, + 0xec2b878c57869a49, + 0xbb47401eb1ff6cf2, + 0x054f245ce7ac1bcd, + ]); +} + +impl ark_ff::fields::FpParameters for FqParameters { + // 0x24000000000024000130e0000d7f70e4a803ca76f439266f443f9a5cda8a6c7be4a7a5fe8fadffd6a2a7e8c30006b9459ffffcd300000001 + const MODULUS: BigInteger = BigInteger([ + 0x9ffffcd300000001, + 0xa2a7e8c30006b945, + 0xe4a7a5fe8fadffd6, + 0x443f9a5cda8a6c7b, + 0xa803ca76f439266f, + 0x0130e0000d7f70e4, + 0x2400000000002400, + ]); + + // R = 2^448 mod q + const R: BigInteger = BigInteger([ + 0xa000163afffffff9, + 0x8d68a2aaffd0ef18, + 0xbf6a760a123e0121, + 0x2242c7760637089c, + 0x67e576bf526ff2f5, + 0xf7a9dfffa183e9bf, + 0x03ffffffffff03ff, + ]); + + // R2 = (2^448)^2 mod q + const R2: BigInteger = BigInteger([ + 0xd9702c6d54dc0598, + 0x4b20c07277ae01f1, + 0x7a42067a8ccd154b, + 0x734fd363b575c23e, + 0x20b6db3d7481a84c, + 0x8bcb0f20758aec85, + 0x1a4b16581f66e3cc, + ]); + + const MODULUS_MINUS_ONE_DIV_TWO: BigInteger = BigInteger([ + 0xcffffe6980000000, + 0x5153f46180035ca2, + 0xf253d2ff47d6ffeb, + 0xa21fcd2e6d45363d, + 0x5401e53b7a1c9337, + 0x0098700006bfb872, + 0x1200000000001200, + ]); + + // T and T_MINUS_ONE_DIV_TWO, where MODULUS - 1 = 2^S * T + + const T: BigInteger = BigInteger([ + 0x0006b9459ffffcd3, + 0x8fadffd6a2a7e8c3, + 0xda8a6c7be4a7a5fe, + 0xf439266f443f9a5c, + 0x0d7f70e4a803ca76, + 0x000024000130e000, + 0x0000000024000000, + ]); + + const T_MINUS_ONE_DIV_TWO: BigInteger = BigInteger([ + 0x80035ca2cffffe69, + 0x47d6ffeb5153f461, + 0x6d45363df253d2ff, + 0x7a1c9337a21fcd2e, + 0x06bfb8725401e53b, + 0x0000120000987000, + 0x0000000012000000, + ]); + + // GENERATOR = 19 + // Encoded in Montgomery form, so the value here is 19R mod q. + const GENERATOR: BigInteger = BigInteger([ + 0xa001acbaffffff79, + 0x3974412afc744c48, + 0x6b9776c23b3e15d0, + 0x02759908c100caaa, + 0x66003b4535dcbb53, + 0x5f39dff8e1cb776b, + 0x03ffffffffed03ff, + ]); + + const MODULUS_BITS: u32 = 446; + + const CAPACITY: u32 = Self::MODULUS_BITS - 1; + + const REPR_SHAVE_BITS: u32 = 2; + + // INV = -q^{-1} (mod 2^64) + const INV: u64 = 11529211554260058111; +} diff --git a/pluto/src/fields/fr.rs b/pluto/src/fields/fr.rs new file mode 100644 index 00000000..293eb9c2 --- /dev/null +++ b/pluto/src/fields/fr.rs @@ -0,0 +1,115 @@ +use ark_ff::{ + biginteger::BigInteger448 as BigInteger, + fields::{FftParameters, Fp448, Fp448Parameters, FpParameters}, +}; + +pub struct FrParameters; + +pub type Fr = Fp448; + +impl Fp448Parameters for FrParameters {} +impl FftParameters for FrParameters { + type BigInt = BigInteger; + + const TWO_ADICITY: u32 = 32; + + // TWO_ADIC_ROOT_OF_UNITY = GENERATOR^T + // Encoded in Montgomery form, so the value here is (5^T)R mod r. + const TWO_ADIC_ROOT_OF_UNITY: BigInteger = BigInteger([ + 0x1f8e3833929b5b76, + 0xefc4b78255f90eb7, + 0xb20ca09993e96198, + 0x8c7b1ccbfc9af2d0, + 0xa064b21566c8da3a, + 0x3af9ec0596cee365, + 0x2319f8cba754ae2d, + ]); +} + +impl FpParameters for FrParameters { + // 0x24000000000024000130e0000d7f70e4a803ca76f439266f443f9a5c7a8a6c7be4a775fe8e177fd69ca7e85d60050af41ffffcd300000001 + const MODULUS: BigInteger = BigInteger([ + 0x1ffffcd300000001, + 0x9ca7e85d60050af4, + 0xe4a775fe8e177fd6, + 0x443f9a5c7a8a6c7b, + 0xa803ca76f439266f, + 0x0130e0000d7f70e4, + 0x2400000000002400, + ]); + + // R = 2^448 mod r + const R: BigInteger = BigInteger([ + 0x2000163afffffff9, + 0xb768a5725fdcb353, + 0xbf6bc60a1d5b8121, + 0x2242c778a637089c, + 0x67e576bf526ff2f5, + 0xf7a9dfffa183e9bf, + 0x03ffffffffff03ff, + ]); + + // R2 = (2^448)^2 mod r + const R2: BigInteger = BigInteger([ + 0x740808c831022522, + 0xbc64e865fe4552ad, + 0x19bd905e6e4ff6c2, + 0x51da4da1c97f7164, + 0x44d51e923f646956, + 0xe436895a5a630ff5, + 0x050d7c998f46144e, + ]); + + const MODULUS_MINUS_ONE_DIV_TWO: BigInteger = BigInteger([ + 0x0ffffe6980000000, + 0x4e53f42eb002857a, + 0xf253baff470bbfeb, + 0xa21fcd2e3d45363d, + 0x5401e53b7a1c9337, + 0x0098700006bfb872, + 0x1200000000001200, + ]); + + // T and T_MINUS_ONE_DIV_TWO, where MODULUS - 1 = 2^S * T + + const T: BigInteger = BigInteger([ + 0x60050af41ffffcd3, + 0x8e177fd69ca7e85d, + 0x7a8a6c7be4a775fe, + 0xf439266f443f9a5c, + 0x0d7f70e4a803ca76, + 0x000024000130e000, + 0x0000000024000000, + ]); + + const T_MINUS_ONE_DIV_TWO: BigInteger = BigInteger([ + 0xb002857a0ffffe69, + 0x470bbfeb4e53f42e, + 0x3d45363df253baff, + 0x7a1c9337a21fcd2e, + 0x06bfb8725401e53b, + 0x0000120000987000, + 0x0000000012000000, + ]); + + // GENERATOR = 19 + // Encoded in Montgomery form, so the value here is 19R mod r. + const GENERATOR: BigInteger = BigInteger([ + 0x2001acbaffffff79, + 0x637476c25d573943, + 0x6bb0c6c3119b95d3, + 0x0275993b6100caaa, + 0x66003b4535dcbb53, + 0x5f39dff8e1cb776b, + 0x03ffffffffed03ff, + ]); + + const MODULUS_BITS: u32 = 446; + + const CAPACITY: u32 = Self::MODULUS_BITS - 1; + + const REPR_SHAVE_BITS: u32 = 2; + + // INV = -r^{-1} (mod 2^64) + const INV: u64 = 2305839517405282303; +} diff --git a/pluto/src/fields/mod.rs b/pluto/src/fields/mod.rs new file mode 100644 index 00000000..00906f27 --- /dev/null +++ b/pluto/src/fields/mod.rs @@ -0,0 +1,12 @@ +#[cfg(feature = "base_field")] +pub mod fq; +#[cfg(feature = "base_field")] +pub use self::fq::*; + +#[cfg(feature = "scalar_field")] +pub mod fr; +#[cfg(feature = "scalar_field")] +pub use self::fr::*; + +#[cfg(all(feature = "curve", test))] +mod tests; diff --git a/pluto/src/fields/tests.rs b/pluto/src/fields/tests.rs new file mode 100644 index 00000000..26807e23 --- /dev/null +++ b/pluto/src/fields/tests.rs @@ -0,0 +1,26 @@ +use ark_std::rand::Rng; +use ark_std::test_rng; + +use crate::*; + +use ark_algebra_test_templates::fields::*; + +#[test] +fn test_fr() { + let mut rng = test_rng(); + let a: Fr = rng.gen(); + let b: Fr = rng.gen(); + field_test(a, b); + sqrt_field_test(a); + primefield_test::(); +} + +#[test] +fn test_fq() { + let mut rng = test_rng(); + let a: Fq = rng.gen(); + let b: Fq = rng.gen(); + field_test(a, b); + sqrt_field_test(a); + primefield_test::(); +} diff --git a/pluto/src/lib.rs b/pluto/src/lib.rs new file mode 100644 index 00000000..1c206edb --- /dev/null +++ b/pluto/src/lib.rs @@ -0,0 +1,36 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![deny( + warnings, + unused, + future_incompatible, + nonstandard_style, + rust_2018_idioms +)] +#![forbid(unsafe_code)] + +//! This library implements the prime-order pairing-friendly curve Pluto, generated +//! by [Daira Hopwood](https://github.com/daira/pluto-eris). This curve is part of a +//! half-pairing cycle with Eris, i.e. its scalar field and base field respectively +//! are the base field and scalar field of Eris. +//! +//! +//! Curve information: +//! * Base field: q = +//! 0x24000000000024000130e0000d7f70e4a803ca76f439266f443f9a5cda8a6c7be4a7a5fe8fadffd6a2a7e8c30006b9459ffffcd300000001 +//! * Scalar field: r = +//! 0x24000000000024000130e0000d7f70e4a803ca76f439266f443f9a5c7a8a6c7be4a775fe8e177fd69ca7e85d60050af41ffffcd300000001 +//! * Curve equation: y^2 = x^3 + 57 +//! * Valuation(q - 1, 2) = 32 +//! * Valuation(r - 1, 2) = 32 + +#[cfg(feature = "r1cs")] +pub mod constraints; +#[cfg(feature = "curve")] +mod curves; +#[cfg(any(feature = "scalar_field", feature = "base_field"))] +mod fields; + +#[cfg(feature = "curve")] +pub use curves::*; +#[cfg(any(feature = "scalar_field", feature = "base_field"))] +pub use fields::*;