Skip to content

Commit 44e4aa4

Browse files
dreamATDhero78119
andauthored
Update Expression<E> and curves to support precomiples (#7)
* Add FieldAlgebra to Expression<E>, Add curves crate * Fix tiny issues * cargo fmt * Add to_limbs_expr function in FieldParameters * Add credits according to the comments --------- Co-authored-by: sm.wu <[email protected]>
1 parent 859a252 commit 44e4aa4

File tree

18 files changed

+2951
-0
lines changed

18 files changed

+2951
-0
lines changed

Cargo.lock

Lines changed: 666 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ members = [
1010
"crates/p3",
1111
"crates/ff_ext",
1212
"crates/whir",
13+
"crates/curves",
1314
]
1415
resolver = "2"
1516

@@ -35,10 +36,13 @@ witness = { path = "crates/witness", default-features = false }
3536
whir = { path = "crates/whir" }
3637

3738
bincode = "1"
39+
cfg-if = "1.0"
3840
clap = { version = "4.5", features = ["derive"] }
3941
criterion = { version = "0.5", features = ["html_reports"] }
4042
either = { version = "1.15.*", features = ["serde"] }
43+
generic-array = { version = "1.1.0", features = ["alloc", "serde"] }
4144
itertools = "0.13"
45+
num = "0.4.3"
4246
p3-baby-bear = { git = "https://github.com/Plonky3/Plonky3.git", rev = "539bbc84085efb609f4f62cb03cf49588388abdb" }
4347
p3-challenger = { git = "https://github.com/Plonky3/Plonky3.git", rev = "539bbc84085efb609f4f62cb03cf49588388abdb" }
4448
p3-commit = { git = "https://github.com/Plonky3/Plonky3.git", rev = "539bbc84085efb609f4f62cb03cf49588388abdb" }

crates/curves/Cargo.toml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
[package]
2+
categories = { workspace = true }
3+
description = "SP1 is a performant, 100% open-source, contributor-friendly zkVM."
4+
edition = { workspace = true }
5+
keywords = { workspace = true }
6+
license = { workspace = true }
7+
name = "sp1-curves"
8+
readme = "../../README.md"
9+
repository = { workspace = true }
10+
version = { workspace = true }
11+
12+
[dependencies]
13+
amcl = { package = "snowbridge-amcl", version = "1.0.2", default-features = false, features = [
14+
"bls381",
15+
] }
16+
dashu = "0.4.2"
17+
elliptic-curve = "0.13.8"
18+
generic-array.workspace = true
19+
k256 = { version = "0.13.3", features = ["expose-field"] }
20+
num.workspace = true
21+
p256 = { version = "0.13.2", features = ["expose-field"] }
22+
p3-field.workspace = true
23+
serde = { workspace = true, features = ["derive"] }
24+
typenum = "1.17.0"
25+
26+
cfg-if.workspace = true
27+
ff_ext = { version = "0.1.0", path = "../ff_ext" }
28+
itertools = { workspace = true }
29+
multilinear_extensions = { version = "0.1.0", path = "../multilinear_extensions" }
30+
rug = { version = "1.26.1", optional = true }
31+
32+
[dev-dependencies]
33+
num = { version = "0.4.3", features = ["rand"] }
34+
rand = "0.8.5"
35+
36+
[features]
37+
bigint-rug = ["rug"]

crates/curves/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# sp1-curves
2+
3+
This crate is modified from [sp1-curves](https://github.com/succinctlabs/sp1/tree/dev/crates/curves).
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
use std::str::FromStr;
2+
3+
use crate::curve25519_dalek::CompressedEdwardsY;
4+
use generic_array::GenericArray;
5+
use num::{BigUint, Num, One};
6+
use serde::{Deserialize, Serialize};
7+
use typenum::{U32, U62};
8+
9+
use crate::{
10+
AffinePoint, CurveType, EllipticCurveParameters,
11+
edwards::{EdwardsCurve, EdwardsParameters},
12+
params::{FieldParameters, NumLimbs},
13+
};
14+
15+
pub type Ed25519 = EdwardsCurve<Ed25519Parameters>;
16+
17+
#[derive(Default, Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
18+
pub struct Ed25519Parameters;
19+
20+
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
21+
pub struct Ed25519BaseField;
22+
23+
impl FieldParameters for Ed25519BaseField {
24+
const MODULUS: &'static [u8] = &[
25+
237, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
26+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127,
27+
];
28+
29+
const WITNESS_OFFSET: usize = 1usize << 14;
30+
31+
fn modulus() -> BigUint {
32+
(BigUint::one() << 255) - BigUint::from(19u32)
33+
}
34+
}
35+
36+
impl NumLimbs for Ed25519BaseField {
37+
type Limbs = U32;
38+
type Witness = U62;
39+
}
40+
41+
impl EllipticCurveParameters for Ed25519Parameters {
42+
type BaseField = Ed25519BaseField;
43+
const CURVE_TYPE: CurveType = CurveType::Ed25519;
44+
}
45+
46+
impl EdwardsParameters for Ed25519Parameters {
47+
const D: GenericArray<u8, U32> = GenericArray::from_array([
48+
163, 120, 89, 19, 202, 77, 235, 117, 171, 216, 65, 65, 77, 10, 112, 0, 152, 232, 121, 119,
49+
121, 64, 199, 140, 115, 254, 111, 43, 238, 108, 3, 82,
50+
]);
51+
52+
fn prime_group_order() -> BigUint {
53+
BigUint::from(2u32).pow(252) + BigUint::from(27742317777372353535851937790883648493u128)
54+
}
55+
56+
fn generator() -> (BigUint, BigUint) {
57+
let x = BigUint::from_str_radix(
58+
"15112221349535400772501151409588531511454012693041857206046113283949847762202",
59+
10,
60+
)
61+
.unwrap();
62+
let y = BigUint::from_str_radix(
63+
"46316835694926478169428394003475163141307993866256225615783033603165251855960",
64+
10,
65+
)
66+
.unwrap();
67+
(x, y)
68+
}
69+
}
70+
71+
/// Computes the square root of a number in the base field of Ed25519.
72+
///
73+
/// This function always returns the nonnegative square root, in the sense that the least
74+
/// significant bit of the result is always 0.
75+
pub fn ed25519_sqrt(a: &BigUint) -> Option<BigUint> {
76+
// Here is a description of how to calculate sqrt in the Curve25519 base field:
77+
// ssh://[email protected]/succinctlabs/curve25519-dalek/blob/
78+
// e2d1bd10d6d772af07cac5c8161cd7655016af6d/curve25519-dalek/src/field.rs#L256
79+
80+
let modulus = Ed25519BaseField::modulus();
81+
// The exponent is (modulus+3)/8;
82+
let mut beta = a.modpow(
83+
&BigUint::from_str(
84+
"7237005577332262213973186563042994240829374041602535252466099000494570602494",
85+
)
86+
.unwrap(),
87+
&modulus,
88+
);
89+
90+
// The square root of -1 in the field.
91+
// Take from here:
92+
// ssh://[email protected]/succinctlabs/curve25519-dalek/blob/
93+
// e2d1bd10d6d772af07cac5c8161cd7655016af6d/curve25519-dalek/src/backend/serial/u64/constants.
94+
// rs#L89
95+
let sqrt_m1 = BigUint::from_str(
96+
"19681161376707505956807079304988542015446066515923890162744021073123829784752",
97+
)
98+
.unwrap();
99+
100+
let beta_squared = &beta * &beta % &modulus;
101+
let neg_a = &modulus - a;
102+
103+
if beta_squared == neg_a {
104+
beta = (&beta * &sqrt_m1) % &modulus;
105+
}
106+
107+
let correct_sign_sqrt = &beta_squared == a;
108+
let flipped_sign_sqrt = beta_squared == neg_a;
109+
110+
if !correct_sign_sqrt && !flipped_sign_sqrt {
111+
return None;
112+
}
113+
114+
let beta_bytes = beta.to_bytes_le();
115+
if (beta_bytes[0] & 1) == 1 {
116+
beta = (&modulus - &beta) % &modulus;
117+
}
118+
119+
Some(beta)
120+
}
121+
122+
pub fn decompress(compressed_point: &CompressedEdwardsY) -> Option<AffinePoint<Ed25519>> {
123+
let mut point_bytes = *compressed_point.as_bytes();
124+
let sign = point_bytes[31] >> 7 == 1;
125+
// mask out the sign bit
126+
point_bytes[31] &= 0b0111_1111;
127+
let modulus = &Ed25519BaseField::modulus();
128+
129+
let y = &BigUint::from_bytes_le(&point_bytes);
130+
let yy = &((y * y) % modulus);
131+
let u = (yy - BigUint::one()) % modulus; // u = y²-1
132+
let v = &((yy * &Ed25519Parameters::d_biguint()) + &BigUint::one()) % modulus; // v = dy²+1
133+
134+
let v_inv = v.modpow(&(modulus - BigUint::from(2u64)), modulus);
135+
let u_div_v = (u * &v_inv) % modulus;
136+
137+
let mut x = ed25519_sqrt(&u_div_v)?;
138+
139+
// sqrt always returns the nonnegative square root,
140+
// so we negate according to the supplied sign bit.
141+
if sign {
142+
x = modulus - &x;
143+
}
144+
145+
Some(AffinePoint::new(x, y.clone()))
146+
}
147+
148+
#[cfg(test)]
149+
mod tests {
150+
151+
use super::*;
152+
use num::traits::ToBytes;
153+
154+
const NUM_TEST_CASES: usize = 100;
155+
156+
#[test]
157+
fn test_ed25519_decompress() {
158+
// This test checks that decompression of generator, 2x generator, 4x generator, etc. works.
159+
160+
// Get the generator point.
161+
let mut point = {
162+
let (x, y) = Ed25519Parameters::generator();
163+
AffinePoint::<EdwardsCurve<Ed25519Parameters>>::new(x, y)
164+
};
165+
for _ in 0..NUM_TEST_CASES {
166+
// Compress the point. The first 255 bits of a compressed point is the y-coordinate. The
167+
// high bit of the 32nd byte gives the "sign" of x, which is the parity.
168+
let compressed_point = {
169+
let x = point.x.to_le_bytes();
170+
let y = point.y.to_le_bytes();
171+
let mut compressed = [0u8; 32];
172+
173+
// Copy y into compressed.
174+
compressed[..y.len()].copy_from_slice(&y);
175+
176+
// Set the sign bit.
177+
compressed[31] |= (x[0] & 1) << 7;
178+
179+
CompressedEdwardsY(compressed)
180+
};
181+
assert_eq!(point, decompress(&compressed_point).unwrap());
182+
183+
// Double the point to create a "random" point for the next iteration.
184+
point = point.clone() + point.clone();
185+
}
186+
}
187+
}

0 commit comments

Comments
 (0)