Skip to content

Adding Stark curve + test large 2-adicity fields #1001

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Pending


- (`ark-starkcurve`) Add 252 bit [Stark curve](https://docs.starknet.io/architecture/cryptography/#the_stark_curve).
- [\#971](https://github.com/arkworks-rs/algebra/pull/971) (`ark-ff`) Make serial_batch_inversion_and_mul public.
- Consolidated logic into `bitreverse_permutation_in_place` and made it public.
- Remove redundant type constraints from `Pairing::G1Prepared`.
Expand Down
2 changes: 2 additions & 0 deletions curves/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ members = [

"curve25519",
"ed25519",

"starkcurve",
]
resolver = "2"

Expand Down
39 changes: 39 additions & 0 deletions curves/starkcurve/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[package]
name = "ark-starkcurve"
version.workspace = true
authors = [ "CPerezz", "arkworks contributors" ]
description = "Stark friendly elliptic curve defined over 2^251 + 17 * 2^192 + 1"
homepage.workspace = true
repository.workspace = true
documentation = "https://docs.rs/ark-starkcurve/"
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-r1cs-std = { workspace = true, optional = true }
ark-std = { workspace = true }
ark-bn254 = { workspace = true, features = [ "scalar_field", "curve" ] }


[dev-dependencies]
ark-relations = { workspace = true }
ark-serialize = { workspace = true }
ark-algebra-test-templates = { workspace = true }
ark-algebra-bench-templates = { workspace = true }
ark-curve-constraint-tests = { path = "../curve-constraint-tests" }

[features]
default = []
std = [ "ark-std/std", "ark-ff/std", "ark-ec/std" ]
r1cs = [ "ark-r1cs-std" ]
asm = [ "ark-ff/asm" ]

[[bench]]
name = "starkcurve"
path = "benches/starkcurve.rs"
harness = false
1 change: 1 addition & 0 deletions curves/starkcurve/LICENSE-APACHE
1 change: 1 addition & 0 deletions curves/starkcurve/LICENSE-MIT
9 changes: 9 additions & 0 deletions curves/starkcurve/benches/starkcurve.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use ark_algebra_bench_templates::*;
use ark_starkcurve::{fq::Fq, fr::Fr, Projective as G};

bench!(
Name = "StarkCurve",
Group = G,
ScalarField = Fr,
PrimeBaseField = Fq,
);
28 changes: 28 additions & 0 deletions curves/starkcurve/scripts/base_field.sage
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
modulus = 3618502788666131213697322783095070105623107215331596699973092056135872020481

assert(modulus.is_prime())

Fp = GF(modulus)

generator = Fp(0);
for i in range(0, 20):
i = Fp(i);
neg_i = Fp(-i)
if not(i.is_primitive_root() or neg_i.is_primitive_root()):
continue
elif i.is_primitive_root():
assert(i.is_primitive_root());
print("Generator: %d" % i)
generator = i
break
else:
assert(neg_i.is_primitive_root());
print("Generator: %d" % neg_i)
generator = neg_i
break


two_adicity = valuation(modulus - 1, 2);
trace = (modulus - 1) / 2**two_adicity;
two_adic_root_of_unity = generator^trace
print("2-adic Root of Unity: %d " % two_adic_root_of_unity)
28 changes: 28 additions & 0 deletions curves/starkcurve/scripts/scalar_field.sage
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
modulus = 3618502788666131213697322783095070105526743751716087489154079457884512865583

assert(modulus.is_prime())

Fp = GF(modulus)

generator = Fp(0);
for i in range(0, 20):
i = Fp(i);
neg_i = Fp(-i)
if not(i.is_primitive_root() or neg_i.is_primitive_root()):
continue
elif i.is_primitive_root():
assert(i.is_primitive_root());
print("Generator: %d" % i)
generator = i
break
else:
assert(neg_i.is_primitive_root());
print("Generator: %d" % neg_i)
generator = neg_i
break


two_adicity = valuation(modulus - 1, 2);
trace = (modulus - 1) / 2**two_adicity;
two_adic_root_of_unity = generator^trace
print("2-adic Root of Unity: %d " % two_adic_root_of_unity)
11 changes: 11 additions & 0 deletions curves/starkcurve/src/constraints/curves.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use ark_r1cs_std::groups::curves::short_weierstrass::ProjectiveVar;

use crate::{constraints::FBaseVar, *};

/// A group element in the StarkCurve prime-order group.
pub type GVar = ProjectiveVar<StarkCurveConfig, FBaseVar>;

#[test]
fn test() {
ark_curve_constraint_tests::curves::sw_test::<StarkCurveConfig, GVar>().unwrap();
}
11 changes: 11 additions & 0 deletions curves/starkcurve/src/constraints/fields.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use ark_r1cs_std::fields::fp::FpVar;

use crate::fq::Fq;

/// A variable that is the R1CS equivalent of `crate::Fq`.
pub type FBaseVar = FpVar<Fq>;

#[test]
fn test() {
ark_curve_constraint_tests::fields::field_test::<_, _, FBaseVar>().unwrap();
}
107 changes: 107 additions & 0 deletions curves/starkcurve/src/constraints/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//! This module implements the R1CS equivalent of `ark_starkcurve`.
//!
//! It implements field variables for `crate::Fq`,
//! and group variables for `crate::Projective`.
//!
//! The field underlying these constraints is `crate::Fq`.
//!
//! # Examples
//!
//! One can perform standard algebraic operations on `FBaseVar`:
//!
//! ```
//! # fn main() -> Result<(), ark_relations::gr1cs::SynthesisError> {
//! use ark_std::UniformRand;
//! use ark_relations::gr1cs::*;
//! use ark_r1cs_std::prelude::*;
//! use ark_starkcurve::{*, constraints::*};
//!
//! let cs = ConstraintSystem::<Fq>::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::gr1cs::SynthesisError> {
//! # use ark_std::UniformRand;
//! # use ark_relations::gr1cs::*;
//! # use ark_r1cs_std::prelude::*;
//! # use ark_starkcurve::{*, constraints::*};
//!
//! # let cs = ConstraintSystem::<Fq>::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::*;
57 changes: 57 additions & 0 deletions curves/starkcurve/src/curves/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// The parameters for the curve have been taken from
// https://github.com/starkware-libs/cairo/blob/main/corelib/src/ec.cairo
// https://docs.starknet.io/architecture/cryptography/

use crate::{Fq, Fr};
use ark_ec::{
models::CurveConfig,
short_weierstrass::{self as sw, SWCurveConfig},
};
use ark_ff::{Field, MontFp};

#[cfg(test)]
mod tests;

#[derive(Copy, Clone, Default, PartialEq, Eq)]
pub struct StarkCurveConfig;

impl CurveConfig for StarkCurveConfig {
type BaseField = Fq;
type ScalarField = Fr;

/// COFACTOR = 1
const COFACTOR: &'static [u64] = &[0x1];

/// COFACTOR_INV = 1
const COFACTOR_INV: Fr = Fr::ONE;
}

pub type Affine = sw::Affine<StarkCurveConfig>;
pub type Projective = sw::Projective<StarkCurveConfig>;

impl SWCurveConfig for StarkCurveConfig {
const COEFF_A: Fq = Fq::ONE;

const COEFF_B: Fq =
MontFp!("3141592653589793238462643383279502884197169399375105820974944592307816406665");

/// AFFINE_GENERATOR_COEFFS = (G1_GENERATOR_X, G1_GENERATOR_Y)
const GENERATOR: Affine = Affine::new_unchecked(G_GENERATOR_X, G_GENERATOR_Y);

#[inline(always)]
fn mul_by_a(elem: Self::BaseField) -> Self::BaseField {
elem
}

/// Correctness:
/// Substituting (0, 0) into the curve equation gives 0^2 = b.
/// Since b is not zero, the point (0, 0) is not on the curve.
/// Therefore, we can safely use (0, 0) as a flag for the zero point.
type ZeroFlag = ();
}

pub const G_GENERATOR_X: Fq =
MontFp!("874739451078007766457464989774322083649278607533249481151382481072868806602");

pub const G_GENERATOR_Y: Fq =
MontFp!("152666792071518830868575557812948353041420400780739481342941381225525861407");
4 changes: 4 additions & 0 deletions curves/starkcurve/src/curves/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
use crate::Projective;
use ark_algebra_test_templates::*;

test_group!(g1; Projective; sw);
7 changes: 7 additions & 0 deletions curves/starkcurve/src/fields/fq.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use ark_ff::fields::{Fp256, MontBackend, MontConfig};

#[derive(MontConfig)]
#[modulus = "3618502788666131213697322783095070105623107215331596699973092056135872020481"]
#[generator = "3"]
pub struct FqConfig;
pub type Fq = Fp256<MontBackend<FqConfig, 4>>;
7 changes: 7 additions & 0 deletions curves/starkcurve/src/fields/fr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use ark_ff::fields::{Fp256, MontBackend, MontConfig};

#[derive(MontConfig)]
#[modulus = "3618502788666131213697322783095070105526743751716087489154079457884512865583"]
#[generator = "3618502788666131213697322783095070105526743751716087489154079457884512865581"]
pub struct FrConfig;
pub type Fr = Fp256<MontBackend<FrConfig, 4>>;
8 changes: 8 additions & 0 deletions curves/starkcurve/src/fields/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
pub mod fq;
pub use self::fq::*;

pub mod fr;
pub use self::fr::*;

#[cfg(test)]
mod tests;
5 changes: 5 additions & 0 deletions curves/starkcurve/src/fields/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use crate::{Fq, Fr};
use ark_algebra_test_templates::*;

test_field!(fq; Fq; mont_prime_field);
test_field!(fr; Fr; mont_prime_field);
38 changes: 38 additions & 0 deletions curves/starkcurve/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#![cfg_attr(not(feature = "std"), no_std)]
#![deny(
warnings,
unused,
future_incompatible,
nonstandard_style,
rust_2018_idioms
)]
#![forbid(unsafe_code)]

//! This library implements [the Stark curve](https://docs.starknet.io/architecture/cryptography/#the_stark_curve)
//! An elliptic curve defined over the STARK field by equation y² ≡ x³ + α·x + β (mod q)
//!
//! Where:
//! * α = 1
//! * β = 3141592653589793238462643383279502884197169399375105820974944592307816406665
//! * q = 3618502788666131213697322783095070105623107215331596699973092056135872020481
//! * or, q = 2^251 + 17 * 2^192 + 1
//!
//! Generator point:
//! * x = 0x1ef15c18599971b7beced415a40f0c7deacfd9b0d1819e03d723d8bc943cfca
//! * y = 0x5668060aa49730b7be4801df46ec62de53ecd11abe43a32873000c36e8dc1f
//!
//! Curve information:
//! StarkCurve:
//! * Base field: q =
//! 3618502788666131213697322783095070105623107215331596699973092056135872020481
//! * Scalar field: r =
//! 3618502788666131213697322783095070105526743751716087489154079457884512865583
//! * Curve equation: y^2 = x^3 + x + β (mod q)

#[cfg(feature = "r1cs")]
pub mod constraints;
mod curves;
mod fields;

pub use curves::*;
pub use fields::*;
Loading
Loading