Skip to content

Commit

Permalink
Merge branch 'main' into support_bls12-381
Browse files Browse the repository at this point in the history
  • Loading branch information
CPerezz authored Jan 17, 2024
2 parents b02d296 + c1745de commit 95a9fc3
Show file tree
Hide file tree
Showing 20 changed files with 381 additions and 139 deletions.
24 changes: 22 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,38 @@ env:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true


jobs:
compat:
if: github.event.pull_request.draft == false
name: Wasm-compatibility
runs-on: ubuntu-latest
strategy:
matrix:
target:
- wasm32-unknown-unknown
- wasm32-wasi
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1

- name: Download WASM targets
run: rustup target add "${{ matrix.target }}"
# We run WASM build (for tests) which compiles the lib allowig us to have
# `getrandom` as a dev-dependency.
- name: Build
run: cargo build --tests --release --features "bn256-table derive_serde prefetch" --target "${{ matrix.target }}"
test:
if: github.event.pull_request.draft == false
name: Test
runs-on: ubuntu-latest
strategy:
matrix:
include:
- feature:
- feature: default
- feature: bn256-table
- feature: derive_serde
- feature: asm
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
Expand Down
12 changes: 8 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "halo2curves"
version = "0.4.0"
version = "0.6.0"
authors = ["Privacy Scaling Explorations team"]
license = "MIT/Apache-2.0"
edition = "2021"
Expand All @@ -18,6 +18,11 @@ serde_json = "1.0.105"
hex = "0.4"
rand_chacha = "0.3.1"

# Added to make sure we are able to build the lib in the CI.
# Notice this will never be loaded for someone using this lib as dep.
[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dev-dependencies]
getrandom = { version = "0.2", features = ["js"] }

[dependencies]
subtle = "2.4"
ff = { version = "0.13.0", default-features = false, features = ["std"] }
Expand All @@ -36,11 +41,10 @@ serde_arrays = { version = "0.1.0", optional = true }
hex = { version = "0.4", optional = true, default-features = false, features = ["alloc", "serde"] }
blake2b_simd = "1"
bls12_381 = { git = "https://github.com/privacy-scaling-explorations/bls12_381", tag = "v2023_10_26", features = ["groups", "basefield"] }
maybe-rayon = { version = "0.1.0", default-features = false }
rayon = "1.8"

[features]
default = ["bits", "multicore"]
multicore = ["maybe-rayon/threads"]
default = ["bits"]
asm = []
bits = ["ff/bits"]
bn256-table = []
Expand Down
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# A collection of Elliptic Curves for ZkCrypto traits

[![crates.io version](https://img.shields.io/crates/v/halo2curves.svg)](https://crates.io/crates/halo2curves)
[![docs.rs availability](https://img.shields.io/docsrs/halo2curves?label=docs.rs)](https://docs.rs/halo2curves)
[![Build status](https://img.shields.io/github/actions/workflow/status/privacy-scaling-explorations/halo2curves/ci.yml?branch=main)](https://github.com/privacy-scaling-explorations/halo2curves/actions)

This library provides efficient and flexible implementations of various halo2-friendly elliptic curves, originally implementing the BN256 curve with traits from the `zkcrypto` ecosystem,

* [`zkcrypto/ff`](https://github.com/zkcrypto/ff)
Expand All @@ -8,11 +12,24 @@ This library provides efficient and flexible implementations of various halo2-fr

The implementations were originally ported from [matterlabs/pairing](https://github.com/matter-labs/pairing/tree/master/src/bn256) and [zkcrypto/bls12-381](https://github.com/zkcrypto/bls12_381), but have been extended and optimized to cover a broader set of curves and use cases. Since its initial release, the library has expanded to include additional curves, along with the following features:

* `secp256k1`, `secp256r1`, and `grumpkin` curves, enhancing its usability across a range of cryptographic protocols.
* `secp256k1`, `secp256r1`, `pluto`, `eris` and `grumpkin` curves, enhancing its usability across a range of cryptographic protocols.
* Assembly optimizations leading to significantly improved performance.
* Various features related to serialization and deserialization of curve points and field elements.
* Curve-specific optimizations and benchmarking capabilities.

## Controlling parallelism

`halo2curves` currently uses [rayon](https://github.com/rayon-rs/rayon) for parallel
computation.

The `RAYON_NUM_THREADS` environment variable can be used to set the number of
threads.

When compiling to WASM-targets, notice that since version `1.7`, `rayon` will fallback automatically (with no need to handle features) to require `getrandom` in order to be able to work.
For more info related to WASM-compilation.

See: [Rayon: Usage with WebAssembly](https://github.com/rayon-rs/rayon#usage-with-webassembly) for more info.

## Benchmarks

Benchmarking is supported through the use of Rust's built-in test framework. Benchmarks can be run without assembly optimizations:
Expand Down Expand Up @@ -42,4 +59,4 @@ The library's top-level directories are organized as follows:

* `benches`: Contains benchmarking tests.
* `script`: Contains utility scripts.
* `src`: Contains the source code of the library, further subdivided into modules for each supported curve (`bn256`, `grumpkin`, `secp256k1`, `secp256r1`, `pasta`) and additional functionalities (`derive`, `tests`).
* `src`: Contains the source code of the library, further subdivided into modules for each supported curve (`bn256`, `grumpkin`, `secp256k1`, `secp256r1`, `secq256k1`, `pasta`, `pluto`, `eris`) and additional functionalities (`derive`, `tests`).
2 changes: 1 addition & 1 deletion src/bn256/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ impl G2Prepared {

r.y -= &tmp2;

// up to here everything was by algorith, line 11
// up to here everything was by algorithm, line 11
// use R instead of new T

// tmp3 is the first part of line 12
Expand Down
4 changes: 2 additions & 2 deletions src/derive/curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ macro_rules! new_curve_impl {
}

fn jacobian_coordinates(&self) -> ($base, $base, $base) {
// Homogenous to Jacobian
// Homogeneous to Jacobian
let x = self.x * self.z;
let y = self.y * self.z.square();
(x, y, self.z)
Expand Down Expand Up @@ -563,7 +563,7 @@ macro_rules! new_curve_impl {
}

fn new_jacobian(x: Self::Base, y: Self::Base, z: Self::Base) -> CtOption<Self> {
// Jacobian to homogenous
// Jacobian to homogeneous
let z_inv = z.invert().unwrap_or($base::zero());
let p_x = x * z_inv;
let p_y = y * z_inv.square();
Expand Down
2 changes: 1 addition & 1 deletion src/derive/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ macro_rules! field_common {
#[cfg(feature = "asm")]
const fn montgomery_form(val: [u64; 4], r: $field) -> $field {
// Converts a 4 64-bit limb value into its congruent field representation.
// If `val` representes a 256 bit value then `r` should be R^2,
// If `val` represents a 256 bit value then `r` should be R^2,
// if `val` represents the 256 MSB of a 512 bit value, then `r` should be R^3.

let (r0, carry) = mac(0, val[0], r.0[0], 0);
Expand Down
2 changes: 1 addition & 1 deletion src/ff_ext/jacobi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ pub fn jacobi<const L: usize>(n: &[u64], d: &[u64]) -> i64 {
// When each "approximation" variable has the same value as the corresponding "precise"
// one, the computation is accomplished using the short-arithmetic method of the Jacobi
// symbol calculation by means of the binary Euclidean algorithm. This approach aims at
// avoiding the parts of the final computations, which are related to long arithmetics
// avoiding the parts of the final computations, which are related to long arithmetic
if precise {
return jacobinary(a, b, t);
}
Expand Down
5 changes: 2 additions & 3 deletions src/fft.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::multicore;
pub use crate::{CurveAffine, CurveExt};
use ff::Field;
use group::{GroupOpsOwned, ScalarMulOwned};
Expand Down Expand Up @@ -38,7 +37,7 @@ pub fn best_fft<Scalar: Field, G: FftGroup<Scalar>>(a: &mut [G], omega: Scalar,
r
}

let threads = multicore::current_num_threads();
let threads = rayon::current_num_threads();
let log_threads = threads.ilog2();
let n = a.len();
assert_eq!(n, 1 << log_n);
Expand Down Expand Up @@ -107,7 +106,7 @@ pub fn recursive_butterfly_arithmetic<Scalar: Field, G: FftGroup<Scalar>>(
a[1] -= &t;
} else {
let (left, right) = a.split_at_mut(n / 2);
multicore::join(
rayon::join(
|| recursive_butterfly_arithmetic(left, n / 2, twiddle_chunk * 2, twiddles),
|| recursive_butterfly_arithmetic(right, n / 2, twiddle_chunk * 2, twiddles),
);
Expand Down
28 changes: 24 additions & 4 deletions src/hash_to_curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ use pasta_curves::arithmetic::CurveExt;
use static_assertions::const_assert;
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};

use crate::ff_ext::Legendre;
use crate::{
ff_ext::Legendre,
secp256k1::{iso_map_secp256k1, IsoSecp256k1, Secp256k1},
};

/// Hashes over a message and writes the output to all of `buf`.
/// Modified from https://github.com/zcash/pasta_curves/blob/7e3fc6a4919f6462a32b79dd226cb2587b7961eb/src/hashtocurve.rs#L11.
Expand Down Expand Up @@ -87,7 +90,7 @@ fn hash_to_field<F: FromUniformBytes<64>>(

// Implementation of <https://datatracker.ietf.org/doc/html/rfc9380#name-simplified-swu-method>
#[allow(clippy::too_many_arguments)]
pub(crate) fn simple_svdw_map_to_curve<C>(u: C::Base, z: C::Base) -> C
pub(crate) fn sswu_map_to_curve<C>(u: C::Base, z: C::Base) -> C
where
C: CurveExt,
{
Expand Down Expand Up @@ -151,8 +154,9 @@ where
C::new_jacobian(x, y, one).unwrap()
}

// Implementation of <https://datatracker.ietf.org/doc/html/rfc9380#name-simplified-swu-method>
#[allow(clippy::type_complexity)]
pub(crate) fn simple_svdw_hash_to_curve<'a, C>(
pub(crate) fn sswu_hash_to_curve<'a, C>(
curve_id: &'static str,
domain_prefix: &'a str,
z: C::Base,
Expand All @@ -165,14 +169,30 @@ where
let mut us = [C::Base::ZERO; 2];
hash_to_field("SSWU", curve_id, domain_prefix, message, &mut us);

let [q0, q1]: [C; 2] = us.map(|u| simple_svdw_map_to_curve(u, z));
let [q0, q1]: [C; 2] = us.map(|u| sswu_map_to_curve::<C>(u, z));

let r = q0 + &q1;
debug_assert!(bool::from(r.is_on_curve()));
r
})
}

// Implementation of <https://datatracker.ietf.org/doc/html/rfc9380#name-simplified-swu-for-ab-0>
#[allow(clippy::type_complexity)]
pub(crate) fn sswu_hash_to_curve_secp256k1<'a>(
_curve_id: &'static str,
domain_prefix: &'a str,
) -> Box<dyn Fn(&[u8]) -> Secp256k1 + 'a> {
Box::new(move |message| {
let rp = IsoSecp256k1::hash_to_curve(domain_prefix)(message);

let r = iso_map_secp256k1(rp);

debug_assert!(bool::from(r.is_on_curve()));
r
})
}

#[allow(clippy::too_many_arguments)]
pub(crate) fn svdw_map_to_curve<C>(
u: C::Base,
Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ pub mod ff_ext;
pub mod fft;
pub mod hash_to_curve;
pub mod msm;
pub mod multicore;
pub mod serde;

pub mod bls12_381;
Expand Down
17 changes: 6 additions & 11 deletions src/msm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ use ff::PrimeField;
use group::Group;
use pasta_curves::arithmetic::CurveAffine;

use crate::multicore;

fn get_booth_index(window_index: usize, window_size: usize, el: &[u8]) -> i32 {
// Booth encoding:
// * step by `window` size
Expand Down Expand Up @@ -155,12 +153,12 @@ pub fn small_multiexp<C: CurveAffine>(coeffs: &[C::Scalar], bases: &[C]) -> C::C
pub fn best_multiexp<C: CurveAffine>(coeffs: &[C::Scalar], bases: &[C]) -> C::Curve {
assert_eq!(coeffs.len(), bases.len());

let num_threads = multicore::current_num_threads();
let num_threads = rayon::current_num_threads();
if coeffs.len() > num_threads {
let chunk = coeffs.len() / num_threads;
let num_chunks = coeffs.chunks(chunk).len();
let mut results = vec![C::Curve::identity(); num_chunks];
multicore::scope(|scope| {
rayon::scope(|scope| {
let chunk = coeffs.len() / num_threads;

for ((coeffs, bases), acc) in coeffs
Expand All @@ -186,10 +184,7 @@ mod test {

use std::ops::Neg;

use crate::{
bn256::{Fr, G1Affine, G1},
multicore,
};
use crate::bn256::{Fr, G1Affine, G1};
use ark_std::{end_timer, start_timer};
use ff::{Field, PrimeField};
use group::{Curve, Group};
Expand All @@ -200,12 +195,12 @@ mod test {
fn best_multiexp<C: CurveAffine>(coeffs: &[C::Scalar], bases: &[C]) -> C::Curve {
assert_eq!(coeffs.len(), bases.len());

let num_threads = multicore::current_num_threads();
let num_threads = rayon::current_num_threads();
if coeffs.len() > num_threads {
let chunk = coeffs.len() / num_threads;
let num_chunks = coeffs.chunks(chunk).len();
let mut results = vec![C::Curve::identity(); num_chunks];
multicore::scope(|scope| {
rayon::scope(|scope| {
let chunk = coeffs.len() / num_threads;

for ((coeffs, bases), acc) in coeffs
Expand All @@ -226,7 +221,7 @@ mod test {
}
}

// keeping older implementation it here for baseline comparision, debugging & benchmarking
// keeping older implementation it here for baseline comparison, debugging & benchmarking
fn multiexp_serial<C: CurveAffine>(coeffs: &[C::Scalar], bases: &[C], acc: &mut C::Curve) {
let coeffs: Vec<_> = coeffs.iter().map(|a| a.to_repr()).collect();

Expand Down
16 changes: 0 additions & 16 deletions src/multicore.rs

This file was deleted.

2 changes: 1 addition & 1 deletion src/pluto_eris/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ impl G2Prepared {

r.y -= &tmp2;

// up to here everything was by algorith, line 11
// up to here everything was by algorithm, line 11
// use R instead of new T

// tmp3 is the first part of line 12
Expand Down
2 changes: 1 addition & 1 deletion src/pluto_eris/fields/fp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ pub struct FpRepr {
}

impl FpRepr {
/// Returns an iterator over the bytes of the canoncial representation of the element.
/// Returns an iterator over the bytes of the canonical representation of the element.
pub fn iter(&self) -> Iter<'_, u8> {
self.repr.iter()
}
Expand Down
2 changes: 1 addition & 1 deletion src/pluto_eris/fields/fq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ pub struct FqRepr {
}

impl FqRepr {
/// Returns an iterator over the bytes of the canoncial representation of the element.
/// Returns an iterator over the bytes of the canonical representation of the element.
pub fn iter(&self) -> Iter<'_, u8> {
self.repr.iter()
}
Expand Down
Loading

0 comments on commit 95a9fc3

Please sign in to comment.