From 39c58df3a624405840a0836671736ac312bcb142 Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Thu, 31 Dec 2020 00:56:00 +0000 Subject: [PATCH] Add Pallas and Vesta curves (#21) Co-authored-by: Ying Tong Lai Co-authored-by: Daira Hopwood Co-authored-by: Pratyush Mishra Co-authored-by: therealyingtong --- Cargo.toml | 3 + README.md | 4 ++ curve-tests/src/fields.rs | 14 ++-- pallas/Cargo.toml | 36 +++++++++++ pallas/src/constraints/curves.rs | 12 ++++ pallas/src/constraints/fields.rs | 10 +++ pallas/src/constraints/mod.rs | 107 +++++++++++++++++++++++++++++++ pallas/src/curves/mod.rs | 49 ++++++++++++++ pallas/src/curves/tests.rs | 39 +++++++++++ pallas/src/fields/fq.rs | 90 ++++++++++++++++++++++++++ pallas/src/fields/fr.rs | 91 ++++++++++++++++++++++++++ pallas/src/fields/mod.rs | 12 ++++ pallas/src/fields/tests.rs | 26 ++++++++ pallas/src/lib.rs | 36 +++++++++++ vesta/Cargo.toml | 33 ++++++++++ vesta/src/constraints/curves.rs | 12 ++++ vesta/src/constraints/fields.rs | 10 +++ vesta/src/constraints/mod.rs | 107 +++++++++++++++++++++++++++++++ vesta/src/curves/mod.rs | 51 +++++++++++++++ vesta/src/curves/tests.rs | 39 +++++++++++ vesta/src/fields/fq.rs | 1 + vesta/src/fields/fr.rs | 1 + vesta/src/fields/mod.rs | 8 +++ vesta/src/fields/tests.rs | 26 ++++++++ vesta/src/lib.rs | 33 ++++++++++ 25 files changed, 840 insertions(+), 10 deletions(-) create mode 100644 pallas/Cargo.toml create mode 100644 pallas/src/constraints/curves.rs create mode 100644 pallas/src/constraints/fields.rs create mode 100644 pallas/src/constraints/mod.rs create mode 100644 pallas/src/curves/mod.rs create mode 100644 pallas/src/curves/tests.rs create mode 100644 pallas/src/fields/fq.rs create mode 100644 pallas/src/fields/fr.rs create mode 100644 pallas/src/fields/mod.rs create mode 100644 pallas/src/fields/tests.rs create mode 100644 pallas/src/lib.rs create mode 100644 vesta/Cargo.toml create mode 100644 vesta/src/constraints/curves.rs create mode 100644 vesta/src/constraints/fields.rs create mode 100644 vesta/src/constraints/mod.rs create mode 100644 vesta/src/curves/mod.rs create mode 100644 vesta/src/curves/tests.rs create mode 100644 vesta/src/fields/fq.rs create mode 100644 vesta/src/fields/fr.rs create mode 100644 vesta/src/fields/mod.rs create mode 100644 vesta/src/fields/tests.rs create mode 100644 vesta/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 995983f..2a4e6ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,9 @@ members = [ "mnt4_753", "mnt6_753", "ed_on_mnt4_298", + + "pallas", + "vesta", ] [profile.release] diff --git a/README.md b/README.md index ecb995a..4f760ba 100644 --- a/README.md +++ b/README.md @@ -29,3 +29,7 @@ This repository contains implementations of some popular elliptic curves. The cu * [`ark-mnt4-753`](mnt4_753): Implements the MNT4-753 pairing-friendly curve. This curve forms a pairing-friendly cycle with MNT6-753 * [`ark-mnt6-753`](mnt6_753): Implements the MNT6-753 pairing-friendly curve. This curve forms a pairing-friendly cycle with MNT4-753 * [`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 diff --git a/curve-tests/src/fields.rs b/curve-tests/src/fields.rs index 20a745e..04d3b0e 100644 --- a/curve-tests/src/fields.rs +++ b/curve-tests/src/fields.rs @@ -417,20 +417,14 @@ pub fn field_serialization_test(buf_size: usize) { #[derive(Default, Clone, Copy, Debug)] struct DummyFlags; impl Flags for DummyFlags { + const BIT_SIZE: usize = 200; + fn u8_bitmask(&self) -> u8 { 0 } - fn from_u8(_value: u8) -> Self { - DummyFlags - } - - fn from_u8_remove_flags(_value: &mut u8) -> Self { - DummyFlags - } - - fn len() -> usize { - 200 + fn from_u8(_value: u8) -> Option { + Some(DummyFlags) } } diff --git a/pallas/Cargo.toml b/pallas/Cargo.toml new file mode 100644 index 0000000..6756a91 --- /dev/null +++ b/pallas/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "ark-pallas" +version = "0.1.0" +authors = [ "Ying Tong Lai", "Daira Hopwood", "O(1) Labs", "arkworks contributors" ] +description = "The Pallas prime-order elliptic curve" +homepage = "https://arkworks.rs" +repository = "https://github.com/arkworks-rs/curves" +documentation = "https://docs.rs/ark-pallas/" +keywords = ["cryptography", "finite fields", "elliptic curves" ] +categories = ["cryptography"] +include = ["Cargo.toml", "src"] +license = "MIT/Apache-2.0" +edition = "2018" + +[dependencies] +ark-ff = { git = "https://github.com/arkworks-rs/algebra", default-features = false } +ark-ec = { git = "https://github.com/arkworks-rs/algebra", default-features = false } +ark-r1cs-std = { git = "https://github.com/arkworks-rs/r1cs-std", default-features = false, optional = true } +ark-std = { git = "https://github.com/arkworks-rs/utils", default-features = false } + +[dev-dependencies] +ark-relations = { git = "https://github.com/arkworks-rs/snark", default-features = false } +ark-serialize = { git = "https://github.com/arkworks-rs/algebra", default-features = false } +ark-curve-tests = { path = "../curve-tests", default-features = false } +ark-curve-constraint-tests = { path = "../curve-constraint-tests", default-features = false } +rand = { version = "0.7", default-features = false } +rand_xorshift = "0.2" + +[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/pallas/src/constraints/curves.rs b/pallas/src/constraints/curves.rs new file mode 100644 index 0000000..4f839b1 --- /dev/null +++ b/pallas/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 Pallas prime-order group. +pub type GVar = ProjectiveVar; + +#[test] +fn test() { + ark_curve_constraint_tests::curves::sw_test::().unwrap(); +} diff --git a/pallas/src/constraints/fields.rs b/pallas/src/constraints/fields.rs new file mode 100644 index 0000000..e2f08b0 --- /dev/null +++ b/pallas/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/pallas/src/constraints/mod.rs b/pallas/src/constraints/mod.rs new file mode 100644 index 0000000..e363da8 --- /dev/null +++ b/pallas/src/constraints/mod.rs @@ -0,0 +1,107 @@ +//! This module implements the R1CS equivalent of `ark_pallas`. +//! +//! 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_pallas::{*, 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_pallas::{*, 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/pallas/src/curves/mod.rs b/pallas/src/curves/mod.rs new file mode 100644 index 0000000..f95edc5 --- /dev/null +++ b/pallas/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 PallasParameters; + +impl ModelParameters for PallasParameters { + type BaseField = Fq; + type ScalarField = Fr; +} + +pub type Affine = GroupAffine; +pub type Projective = GroupProjective; + +impl SWModelParameters for PallasParameters { + /// COEFF_A = 0 + const COEFF_A: Fq = field_new!(Fq, "0"); + + /// COEFF_B = 5 + const COEFF_B: Fq = field_new!(Fq, "5"); + + /// 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 = -1 +pub const G_GENERATOR_X: Fq = field_new!(Fq, "-1"); + +/// G_GENERATOR_Y = 2 +pub const G_GENERATOR_Y: Fq = field_new!(Fq, "2"); diff --git a/pallas/src/curves/tests.rs b/pallas/src/curves/tests.rs new file mode 100644 index 0000000..9b95026 --- /dev/null +++ b/pallas/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 core::ops::{AddAssign, MulAssign}; +use rand::Rng; + +use crate::{Affine, PallasParameters, Projective}; + +use ark_curve_tests::{ + 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/pallas/src/fields/fq.rs b/pallas/src/fields/fq.rs new file mode 100644 index 0000000..a94f8ab --- /dev/null +++ b/pallas/src/fields/fq.rs @@ -0,0 +1,90 @@ +use ark_ff::{ + biginteger::BigInteger256 as BigInteger, + fields::{FftParameters, Fp256, Fp256Parameters}, +}; + +pub type Fq = Fp256; + +pub struct FqParameters; + +impl Fp256Parameters 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 (5^T)R mod p. + const TWO_ADIC_ROOT_OF_UNITY: BigInteger = BigInteger([ + 0xa28db849bad6dbf0, + 0x9083cd03d3b539df, + 0xfba6b9ca9dc8448e, + 0x3ec928747b89c6da, + ]); +} + +impl ark_ff::fields::FpParameters for FqParameters { + // 28948022309329048855892746252171976963363056481941560715954676764349967630337 + const MODULUS: BigInteger = BigInteger([ + 0x992d30ed00000001, + 0x224698fc094cf91b, + 0x0000000000000000, + 0x4000000000000000, + ]); + + // R = 2^256 mod p + const R: BigInteger = BigInteger([ + 0x34786d38fffffffd, + 0x992c350be41914ad, + 0xffffffffffffffff, + 0x3fffffffffffffff, + ]); + + // R2 = (2^256)^2 mod p + const R2: BigInteger = BigInteger([ + 0x8c78ecb30000000f, + 0xd7d30dbd8b0de0e7, + 0x7797a99bc3c95d18, + 0x096d41af7b9cb714, + ]); + + const MODULUS_MINUS_ONE_DIV_TWO: BigInteger = BigInteger([ + 0xcc96987680000000, + 0x11234c7e04a67c8d, + 0x0000000000000000, + 0x2000000000000000, + ]); + + // T and T_MINUS_ONE_DIV_TWO, where MODULUS - 1 = 2^S * T + const T: BigInteger = BigInteger([ + 0x094cf91b992d30ed, + 0x00000000224698fc, + 0x0000000000000000, + 0x0000000040000000, + ]); + + const T_MINUS_ONE_DIV_TWO: BigInteger = BigInteger([ + 0x04a67c8dcc969876, + 0x0000000011234c7e, + 0x0000000000000000, + 0x0000000020000000, + ]); + + // GENERATOR = 5 + // Encoded in Montgomery form, so the value here is 5R mod p. + const GENERATOR: BigInteger = BigInteger([ + 0xa1a55e68ffffffed, + 0x74c2a54b4f4982f3, + 0xfffffffffffffffd, + 0x3fffffffffffffff, + ]); + + const MODULUS_BITS: u32 = 255; + + const CAPACITY: u32 = Self::MODULUS_BITS - 1; + + const REPR_SHAVE_BITS: u32 = 1; + + // INV = -p^{-1} (mod 2^64) + const INV: u64 = 11037532056220336127; +} diff --git a/pallas/src/fields/fr.rs b/pallas/src/fields/fr.rs new file mode 100644 index 0000000..f13a78b --- /dev/null +++ b/pallas/src/fields/fr.rs @@ -0,0 +1,91 @@ +use ark_ff::{ + biginteger::BigInteger256 as BigInteger, + fields::{FftParameters, Fp256, Fp256Parameters, FpParameters}, +}; + +pub struct FrParameters; + +pub type Fr = Fp256; + +impl Fp256Parameters 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 q. + const TWO_ADIC_ROOT_OF_UNITY: BigInteger = BigInteger([ + 0x218077428c9942de, + 0xcc49578921b60494, + 0xac2e5d27b2efbee2, + 0x0b79fa897f2db056, + ]); +} + +impl FpParameters for FrParameters { + // 28948022309329048855892746252171976963363056481941647379679742748393362948097 + const MODULUS: BigInteger = BigInteger([ + 0x8c46eb2100000001, + 0x224698fc0994a8dd, + 0x0000000000000000, + 0x4000000000000000, + ]); + + // R = 2^256 mod q + const R: BigInteger = BigInteger([ + 0x5b2b3e9cfffffffd, + 0x992c350be3420567, + 0xffffffffffffffff, + 0x3fffffffffffffff, + ]); + + // R2 = (2^256)^2 mod q + const R2: BigInteger = BigInteger([ + 0xfc9678ff0000000f, + 0x67bb433d891a16e3, + 0x7fae231004ccf590, + 0x096d41af7ccfdaa9, + ]); + + const MODULUS_MINUS_ONE_DIV_TWO: BigInteger = BigInteger([ + 0xc623759080000000, + 0x11234c7e04ca546e, + 0x0000000000000000, + 0x2000000000000000, + ]); + + // T and T_MINUS_ONE_DIV_TWO, where MODULUS - 1 = 2^S * T + + const T: BigInteger = BigInteger([ + 0x0994a8dd8c46eb21, + 0x00000000224698fc, + 0x0000000000000000, + 0x0000000040000000, + ]); + + const T_MINUS_ONE_DIV_TWO: BigInteger = BigInteger([ + 0x04ca546ec6237590, + 0x0000000011234c7e, + 0x0000000000000000, + 0x0000000020000000, + ]); + + // GENERATOR = 5 + // Encoded in Montgomery form, so the value here is 5R mod q. + const GENERATOR: BigInteger = BigInteger([ + 0x96bc8c8cffffffed, + 0x74c2a54b49f7778e, + 0xfffffffffffffffd, + 0x3fffffffffffffff, + ]); + + const MODULUS_BITS: u32 = 255; + + const CAPACITY: u32 = Self::MODULUS_BITS - 1; + + const REPR_SHAVE_BITS: u32 = 1; + + // INV = -q^{-1} (mod 2^64) + const INV: u64 = 10108024940646105087; +} diff --git a/pallas/src/fields/mod.rs b/pallas/src/fields/mod.rs new file mode 100644 index 0000000..00906f2 --- /dev/null +++ b/pallas/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/pallas/src/fields/tests.rs b/pallas/src/fields/tests.rs new file mode 100644 index 0000000..053ad85 --- /dev/null +++ b/pallas/src/fields/tests.rs @@ -0,0 +1,26 @@ +use ark_std::test_rng; +use rand::Rng; + +use crate::*; + +use ark_curve_tests::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/pallas/src/lib.rs b/pallas/src/lib.rs new file mode 100644 index 0000000..ccbfc34 --- /dev/null +++ b/pallas/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 curve Pallas, generated by +//! [Daira Hopwood](https://github.com/zcash/pasta). The main feature of this +//! curve is that it forms a cycle with Vesta, i.e. its scalar field and base +//! field respectively are the base field and scalar field of Vesta. +//! +//! +//! Curve information: +//! * Base field: q = +//! 28948022309329048855892746252171976963363056481941560715954676764349967630337 +//! * Scalar field: r = +//! 28948022309329048855892746252171976963363056481941647379679742748393362948097 +//! * Curve equation: y^2 = x^3 + 5 +//! * 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::*; diff --git a/vesta/Cargo.toml b/vesta/Cargo.toml new file mode 100644 index 0000000..49e1df5 --- /dev/null +++ b/vesta/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "ark-vesta" +version = "0.1.0" +authors = [ "Ying Tong Lai", "Daira Hopwood", "O(1) Labs", "arkworks contributors" ] +description = "The Vesta prime-order elliptic curve" +homepage = "https://arkworks.rs" +repository = "https://github.com/arkworks-rs/curves" +documentation = "https://docs.rs/ark-vesta/" +keywords = ["cryptography", "finite fields", "elliptic curves" ] +categories = ["cryptography"] +include = ["Cargo.toml", "src"] +license = "MIT/Apache-2.0" +edition = "2018" + +[dependencies] +ark-ff = { git = "https://github.com/arkworks-rs/algebra", default-features = false } +ark-ec = { git = "https://github.com/arkworks-rs/algebra", default-features = false } +ark-r1cs-std = { git = "https://github.com/arkworks-rs/r1cs-std", default-features = false, optional = true } +ark-std = { git = "https://github.com/arkworks-rs/utils", default-features = false } +ark-pallas = { path = "../pallas", default-features = false, features = [ "scalar_field", "base_field" ] } + +[dev-dependencies] +ark-relations = { git = "https://github.com/arkworks-rs/snark", default-features = false } +ark-serialize = { git = "https://github.com/arkworks-rs/algebra", default-features = false } +ark-curve-tests = { path = "../curve-tests", default-features = false } +ark-curve-constraint-tests = { path = "../curve-constraint-tests", default-features = false } +rand = { version = "0.7", default-features = false } +rand_xorshift = "0.2" + +[features] +default = [] +std = [ "ark-std/std", "ark-ff/std", "ark-ec/std" ] +r1cs = [ "ark-r1cs-std" ] diff --git a/vesta/src/constraints/curves.rs b/vesta/src/constraints/curves.rs new file mode 100644 index 0000000..fac522a --- /dev/null +++ b/vesta/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 Vesta prime-order group. +pub type GVar = ProjectiveVar; + +#[test] +fn test() { + ark_curve_constraint_tests::curves::sw_test::().unwrap(); +} diff --git a/vesta/src/constraints/fields.rs b/vesta/src/constraints/fields.rs new file mode 100644 index 0000000..e2f08b0 --- /dev/null +++ b/vesta/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/vesta/src/constraints/mod.rs b/vesta/src/constraints/mod.rs new file mode 100644 index 0000000..a2ac129 --- /dev/null +++ b/vesta/src/constraints/mod.rs @@ -0,0 +1,107 @@ +//! This module implements the R1CS equivalent of `ark_vesta`. +//! +//! 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_vesta::{*, 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_vesta::{*, 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/vesta/src/curves/mod.rs b/vesta/src/curves/mod.rs new file mode 100644 index 0000000..865e459 --- /dev/null +++ b/vesta/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 VestaParameters; + +impl ModelParameters for VestaParameters { + type BaseField = Fq; + type ScalarField = Fr; +} + +pub type Affine = GroupAffine; +pub type Projective = GroupProjective; + +impl SWModelParameters for VestaParameters { + /// COEFF_A = 0 + const COEFF_A: Fq = field_new!(Fq, "0"); + + /// COEFF_B = 5 + const COEFF_B: Fq = field_new!(Fq, "5"); + + /// 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 = -1 +/// Encoded in Montgomery form, so the value here is -R mod p. +pub const G_GENERATOR_X: Fq = field_new!(Fq, "-1"); + +/// G_GENERATOR_Y = 2 +/// Encoded in Montgomery form, so the value here is 2R mod p. +pub const G_GENERATOR_Y: Fq = field_new!(Fq, "2"); diff --git a/vesta/src/curves/tests.rs b/vesta/src/curves/tests.rs new file mode 100644 index 0000000..e980de9 --- /dev/null +++ b/vesta/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 core::ops::{AddAssign, MulAssign}; +use rand::Rng; + +use crate::{Affine, Projective, VestaParameters}; + +use ark_curve_tests::{ + 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/vesta/src/fields/fq.rs b/vesta/src/fields/fq.rs new file mode 100644 index 0000000..1346b1a --- /dev/null +++ b/vesta/src/fields/fq.rs @@ -0,0 +1 @@ +pub use ark_pallas::{Fr as Fq, FrParameters as FqParameters}; diff --git a/vesta/src/fields/fr.rs b/vesta/src/fields/fr.rs new file mode 100644 index 0000000..b8207ec --- /dev/null +++ b/vesta/src/fields/fr.rs @@ -0,0 +1 @@ +pub use ark_pallas::{Fq as Fr, FqParameters as FrParameters}; diff --git a/vesta/src/fields/mod.rs b/vesta/src/fields/mod.rs new file mode 100644 index 0000000..5156179 --- /dev/null +++ b/vesta/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/vesta/src/fields/tests.rs b/vesta/src/fields/tests.rs new file mode 100644 index 0000000..053ad85 --- /dev/null +++ b/vesta/src/fields/tests.rs @@ -0,0 +1,26 @@ +use ark_std::test_rng; +use rand::Rng; + +use crate::*; + +use ark_curve_tests::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/vesta/src/lib.rs b/vesta/src/lib.rs new file mode 100644 index 0000000..6b1d64b --- /dev/null +++ b/vesta/src/lib.rs @@ -0,0 +1,33 @@ +#![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 Vesta, generated by +//! [Daira Hopwood](https://github.com/zcash/pasta). The main feature of this +//! curve is that it forms a cycle with Pallas, i.e. its scalar field and base +//! field respectively are the base field and scalar field of Pallas. +//! +//! +//! Curve information: +//! Vesta: +//! * Base field: q = +//! 28948022309329048855892746252171976963363056481941647379679742748393362948097 +//! * Scalar field: r = +//! 28948022309329048855892746252171976963363056481941560715954676764349967630337 +//! * Curve equation: y^2 = x^3 + 5 +//! * 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::*;