Skip to content

Commit 6e33e20

Browse files
authored
perf+feat: generalize and constify Pedersen hash (#851)
Slightly more generic: instead of being implemented only for the STARK curve, make it generic over curves on the STARK252 prime field. Future work may make it work for more fields. Initialization is much faster now: all parameters are computed at compile time.
1 parent 0cd3ef5 commit 6e33e20

File tree

3 files changed

+74
-70
lines changed

3 files changed

+74
-70
lines changed

crypto/benches/criterion_pedersen.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use criterion::{black_box, criterion_group, criterion_main, Criterion};
22
use lambdaworks_crypto::hash::pedersen::Pedersen;
3+
use lambdaworks_crypto::hash::pedersen::PedersenStarkCurve;
34
use lambdaworks_math::field::element::FieldElement;
45
use lambdaworks_math::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField;
56
use lambdaworks_math::traits::ByteConversion;
@@ -16,11 +17,10 @@ fn pedersen_benchmarks(c: &mut Criterion) {
1617
let x = FieldElement::<Stark252PrimeField>::from_bytes_be(&felt1).unwrap();
1718
let y = FieldElement::<Stark252PrimeField>::from_bytes_be(&felt2).unwrap();
1819
let mut group = c.benchmark_group("Pedersen Benchmark");
19-
let pedersen = black_box(Pedersen::default());
2020

2121
// Benchmark with black_box is 0.41% faster
2222
group.bench_function("Hashing with black_box", |bench| {
23-
bench.iter(|| black_box(pedersen.hash(&x, &y)))
23+
bench.iter(|| black_box(PedersenStarkCurve::hash(&x, &y)))
2424
});
2525
}
2626
criterion_group!(pedersen, pedersen_benchmarks);

crypto/src/hash/pedersen/mod.rs

Lines changed: 38 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,66 @@
11
use lambdaworks_math::{
2-
elliptic_curve::short_weierstrass::{
3-
curves::stark_curve::StarkCurve, point::ShortWeierstrassProjectivePoint,
4-
},
2+
elliptic_curve::short_weierstrass::point::ShortWeierstrassProjectivePoint as Point,
53
field::{
6-
element::FieldElement, fields::fft_friendly::stark_252_prime_field::Stark252PrimeField,
4+
element::FieldElement as FE,
5+
fields::fft_friendly::stark_252_prime_field::Stark252PrimeField,
76
},
87
};
98

109
mod constants;
1110
mod parameters;
12-
use self::parameters::PedersenParameters;
11+
use parameters::PedersenParameters;
12+
pub use parameters::PedersenStarkCurve;
1313

14-
pub struct Pedersen {
15-
params: PedersenParameters,
16-
}
14+
mod private {
15+
use super::*;
1716

18-
impl Default for Pedersen {
19-
fn default() -> Self {
20-
let pedersen_stark_default_params = PedersenParameters::default();
21-
Self::new_with_params(pedersen_stark_default_params)
22-
}
23-
}
17+
pub trait Sealed {}
2418

25-
impl Pedersen {
26-
pub fn new_with_params(params: PedersenParameters) -> Self {
27-
Self { params }
28-
}
29-
30-
// Taken from Jonathan Lei's starknet-rs
31-
// https://github.com/xJonathanLEI/starknet-rs/blob/4ab2f36872435ce57b1d8f55856702a6a30f270a/starknet-crypto/src/pedersen_hash.rs
19+
impl<P: PedersenParameters> Sealed for P {}
20+
}
3221

22+
pub trait Pedersen: PedersenParameters + self::private::Sealed {
3323
/// Implements Starkware version of Pedersen hash of x and y.
3424
/// Divides each of x and y into 4-bit chunks, and uses lookup tables to accumulate pre-calculated
3525
/// points corresponding to a given chunk.
3626
/// Accumulation starts from a "shift_point" whose points are derived from digits of pi.
3727
/// Pre-calculated points are multiples by powers of 2 of the "shift_point".
3828
///
3929
/// Find specification at https://docs.starkware.co/starkex/crypto/pedersen-hash-function.html
40-
pub fn hash(
41-
&self,
42-
x: &FieldElement<Stark252PrimeField>,
43-
y: &FieldElement<Stark252PrimeField>,
44-
) -> FieldElement<Stark252PrimeField> {
30+
fn hash(x: &FE<Self::F>, y: &FE<Self::F>) -> FE<Self::F>;
31+
32+
/// Performs lookup to find the constant point corresponding to 4-bit chunks of given input.
33+
/// Keeps adding up those points to the given accumulation point.
34+
fn lookup_and_accumulate(acc: &mut Point<Self::EC>, bits: &[bool], prep: &[Point<Self::EC>]);
35+
}
36+
37+
// FIXME: currently we make some assumptions that apply to `Stark252PrimeField`, so only mark the
38+
// implementation when that's the field.
39+
impl<P: PedersenParameters<F = Stark252PrimeField>> Pedersen for P {
40+
// Taken from Jonathan Lei's starknet-rs
41+
// https://github.com/xJonathanLEI/starknet-rs/blob/4ab2f36872435ce57b1d8f55856702a6a30f270a/starknet-crypto/src/pedersen_hash.rs
42+
43+
fn hash(x: &FE<Self::F>, y: &FE<Self::F>) -> FE<Self::F> {
4544
let x = x.to_bits_le();
4645
let y = y.to_bits_le();
47-
let mut acc = self.params.shift_point.clone();
46+
let mut acc = P::SHIFT_POINT.clone();
4847

49-
self.lookup_and_accumulate(&mut acc, &x[..248], &self.params.points_p1); // Add a_low * P1
50-
self.lookup_and_accumulate(&mut acc, &x[248..252], &self.params.points_p2); // Add a_high * P2
51-
self.lookup_and_accumulate(&mut acc, &y[..248], &self.params.points_p3); // Add b_low * P3
52-
self.lookup_and_accumulate(&mut acc, &y[248..252], &self.params.points_p4); // Add b_high * P4
48+
Self::lookup_and_accumulate(&mut acc, &x[..248], &P::POINTS_P1); // Add a_low * P1
49+
Self::lookup_and_accumulate(&mut acc, &x[248..252], &P::POINTS_P2); // Add a_high * P2
50+
Self::lookup_and_accumulate(&mut acc, &y[..248], &P::POINTS_P3); // Add b_low * P3
51+
Self::lookup_and_accumulate(&mut acc, &y[248..252], &P::POINTS_P4); // Add b_high * P4
5352

5453
*acc.to_affine().x()
5554
}
5655

57-
/// Performs lookup to find the constant point corresponding to 4-bit chunks of given input.
58-
/// Keeps adding up those points to the given accumulation point.
59-
fn lookup_and_accumulate(
60-
&self,
61-
acc: &mut ShortWeierstrassProjectivePoint<StarkCurve>,
62-
bits: &[bool],
63-
prep: &[ShortWeierstrassProjectivePoint<StarkCurve>],
64-
) {
65-
bits.chunks(self.params.curve_const_bits)
56+
fn lookup_and_accumulate(acc: &mut Point<Self::EC>, bits: &[bool], prep: &[Point<Self::EC>]) {
57+
bits.chunks(P::CURVE_CONST_BITS)
6658
.enumerate()
6759
.for_each(|(i, v)| {
6860
let offset = bools_to_usize_le(v);
6961
if offset > 0 {
7062
// Table lookup at 'offset-1' in table for chunk 'i'
71-
*acc = acc.operate_with_affine(&prep[i * self.params.table_size + offset - 1]);
63+
*acc = acc.operate_with_affine(&prep[i * P::TABLE_SIZE + offset - 1]);
7264
}
7365
})
7466
}
@@ -88,24 +80,23 @@ fn bools_to_usize_le(bools: &[bool]) -> usize {
8880
#[cfg(test)]
8981
mod tests {
9082
use super::*;
83+
use crate::hash::pedersen::parameters::PedersenStarkCurve;
9184

9285
// Test case ported from:
9386
// https://github.com/starkware-libs/crypto-cpp/blob/95864fbe11d5287e345432dbe1e80dea3c35fc58/src/starkware/crypto/ffi/crypto_lib_test.go
9487

9588
#[test]
9689
fn test_stark_curve() {
97-
let pedersen = Pedersen::default();
98-
99-
let x = FieldElement::<Stark252PrimeField>::from_hex_unchecked(
90+
let x = FE::from_hex_unchecked(
10091
"03d937c035c878245caf64531a5756109c53068da139362728feb561405371cb",
10192
);
102-
let y = FieldElement::<Stark252PrimeField>::from_hex_unchecked(
93+
let y = FE::from_hex_unchecked(
10394
"0208a0a10250e382e1e4bbe2880906c2791bf6275695e02fbbc6aeff9cd8b31a",
10495
);
105-
let hash = pedersen.hash(&x, &y);
96+
let hash = PedersenStarkCurve::hash(&x, &y);
10697
assert_eq!(
10798
hash,
108-
FieldElement::<Stark252PrimeField>::from_hex_unchecked(
99+
FE::from_hex_unchecked(
109100
"030e480bed5fe53fa909cc0f8c4d99b8f9f2c016be4c41e13a4848797979c662"
110101
)
111102
);
Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,49 @@
1+
use lambdaworks_math::elliptic_curve::short_weierstrass::traits::IsShortWeierstrass;
12
use lambdaworks_math::elliptic_curve::short_weierstrass::{
2-
curves::stark_curve::StarkCurve, point::ShortWeierstrassProjectivePoint,
3+
curves::stark_curve::StarkCurve, point::ShortWeierstrassProjectivePoint as Point,
34
};
5+
use lambdaworks_math::elliptic_curve::traits::IsEllipticCurve;
6+
use lambdaworks_math::field::fields::fft_friendly::stark_252_prime_field::Stark252PrimeField;
7+
use lambdaworks_math::field::traits::IsPrimeField;
48

59
use crate::hash::pedersen::constants::*;
610

7-
pub struct PedersenParameters {
8-
pub curve_const_bits: usize,
9-
pub table_size: usize,
10-
pub shift_point: ShortWeierstrassProjectivePoint<StarkCurve>,
11-
pub points_p1: [ShortWeierstrassProjectivePoint<StarkCurve>; 930],
12-
pub points_p2: [ShortWeierstrassProjectivePoint<StarkCurve>; 15],
13-
pub points_p3: [ShortWeierstrassProjectivePoint<StarkCurve>; 930],
14-
pub points_p4: [ShortWeierstrassProjectivePoint<StarkCurve>; 15],
11+
pub trait PedersenParameters {
12+
type F: IsPrimeField + Clone;
13+
type EC: IsEllipticCurve<BaseField = Self::F> + IsShortWeierstrass + Clone;
14+
15+
const CURVE_CONST_BITS: usize;
16+
const TABLE_SIZE: usize;
17+
const SHIFT_POINT: Point<Self::EC>;
18+
const POINTS_P1: [Point<Self::EC>; 930];
19+
const POINTS_P2: [Point<Self::EC>; 15];
20+
const POINTS_P3: [Point<Self::EC>; 930];
21+
const POINTS_P4: [Point<Self::EC>; 15];
1522
}
1623

17-
impl Default for PedersenParameters {
24+
pub struct PedersenStarkCurve;
25+
26+
impl Default for PedersenStarkCurve {
1827
fn default() -> Self {
1928
Self::new()
2029
}
2130
}
2231

23-
impl PedersenParameters {
32+
impl PedersenStarkCurve {
2433
pub fn new() -> Self {
25-
let curve_const_bits = 4;
26-
Self {
27-
curve_const_bits,
28-
table_size: (1 << curve_const_bits) - 1,
29-
shift_point: shift_point(),
30-
points_p1: points_p1(),
31-
points_p2: points_p2(),
32-
points_p3: points_p3(),
33-
points_p4: points_p4(),
34-
}
34+
Self {}
3535
}
3636
}
37+
38+
impl PedersenParameters for PedersenStarkCurve {
39+
type F = Stark252PrimeField;
40+
type EC = StarkCurve;
41+
42+
const CURVE_CONST_BITS: usize = 4;
43+
const TABLE_SIZE: usize = (1 << Self::CURVE_CONST_BITS) - 1;
44+
const SHIFT_POINT: Point<Self::EC> = shift_point();
45+
const POINTS_P1: [Point<Self::EC>; 930] = points_p1();
46+
const POINTS_P2: [Point<Self::EC>; 15] = points_p2();
47+
const POINTS_P3: [Point<Self::EC>; 930] = points_p3();
48+
const POINTS_P4: [Point<Self::EC>; 15] = points_p4();
49+
}

0 commit comments

Comments
 (0)