Skip to content

Commit f7ea44e

Browse files
committed
Merge remote-tracking branch 'bitvm/main' into optimize-polynomials
2 parents 142ea65 + ef00825 commit f7ea44e

File tree

2 files changed

+112
-21
lines changed

2 files changed

+112
-21
lines changed

src/cac/mod.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,18 @@ mod tests {
1818
let n = 181;
1919
let k = 181 - 7;
2020

21+
let secp = vsss::Secp256k1::new();
22+
2123
let mut rng = rand::thread_rng();
2224

2325
// step 1: garbler generates secret:
2426
let polynomial = vsss::Polynomial::rand(&mut rng, k);
2527

2628
// step 2: garbler send commitments to the polynomial coefficients to the evaluator
27-
let coefficient_commits = polynomial.coefficient_commits();
29+
let coefficient_commits = polynomial.coefficient_commits(&secp);
2830

2931
// step 3: garbler shares commits, sends them to evaluator
30-
let share_commits = polynomial.share_commits(n);
32+
let share_commits = polynomial.share_commits(&secp, n);
3133

3234
// step 4: evaluator verifies correctness of the share commits:
3335
share_commits
@@ -46,7 +48,7 @@ mod tests {
4648

4749
// step 7: evaluator checks that the selected shares match the share commits
4850
share_commits
49-
.verify_shares(&selected_shares)
51+
.verify_shares(&secp, &selected_shares)
5052
.expect("Share verification failed");
5153

5254
// step 8: (omitted) evaluator checks the garbled circuit validity

src/cac/vsss.rs

Lines changed: 107 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,62 @@
11
use std::ops::{Add, Mul};
22

3-
use ark_ec::PrimeGroup;
3+
use ark_ec::{PrimeGroup, scalar_mul::BatchMulPreprocessing};
4+
use ark_ff::BigInteger;
5+
use ark_ff::PrimeField;
46
use ark_ff::{Field, One, UniformRand, Zero};
57
use ark_secp256k1::{Fr, Projective};
68
use rand::Rng;
79
use serde::{Deserialize, Serialize};
810

11+
pub struct Secp256k1 {
12+
pub generator: BatchMulPreprocessing<Projective>,
13+
}
14+
15+
impl Default for Secp256k1 {
16+
fn default() -> Self {
17+
Self::new()
18+
}
19+
}
20+
21+
impl Secp256k1 {
22+
pub fn new() -> Self {
23+
// 181*176*3 is roughly the number of generator multiplications we do
24+
Self {
25+
generator: BatchMulPreprocessing::new(Projective::generator(), 181 * 176 * 3),
26+
}
27+
}
28+
29+
// Replacement of BatchMulPreprocessing::batch_mul, which (1) uses rayon parallelization
30+
// and (2) converts the result to an affine point instead of a projective point.
31+
fn generator_batch_mul(&self, scalars: &[Fr]) -> Vec<Projective> {
32+
scalars.iter().map(|e| self.windowed_mul(e)).collect()
33+
}
34+
35+
// copied from ark-ec/src/scalar_mul/mod.rs because it's not public
36+
fn windowed_mul(&self, scalar: &Fr) -> Projective {
37+
let outerc = self
38+
.generator
39+
.max_scalar_size
40+
.div_ceil(self.generator.window);
41+
let modulus_size = Fr::MODULUS_BIT_SIZE as usize;
42+
let scalar_val = scalar.into_bigint().to_bits_le();
43+
44+
let mut res = Projective::from(self.generator.table[0][0]);
45+
for outer in 0..outerc {
46+
let mut inner = 0usize;
47+
for i in 0..self.generator.window {
48+
if outer * self.generator.window + i < modulus_size
49+
&& scalar_val[outer * self.generator.window + i]
50+
{
51+
inner |= 1 << i;
52+
}
53+
}
54+
res += &self.generator.table[outer][inner];
55+
}
56+
res
57+
}
58+
}
59+
960
// we use this for both polynomials over scalars and over projective points
1061
#[derive(Clone, Debug, Serialize, Deserialize)]
1162
pub struct Polynomial<T>(Vec<T>);
@@ -34,9 +85,8 @@ impl Polynomial<Fr> {
3485
Self((0..degree + 1).map(|_| Fr::rand(&mut rand)).collect())
3586
}
3687

37-
pub fn coefficient_commits(&self) -> PolynomialCommits {
38-
let generator = Projective::generator();
39-
PolynomialCommits(Polynomial(self.0.iter().map(|x| generator * x).collect()))
88+
pub fn coefficient_commits(&self, secp: &Secp256k1) -> PolynomialCommits {
89+
PolynomialCommits(Polynomial(secp.generator_batch_mul(&self.0)))
4090
}
4191

4292
// shares are return with 0-based index. However, we evaluate share i at
@@ -47,13 +97,14 @@ impl Polynomial<Fr> {
4797
.collect()
4898
}
4999

50-
pub fn share_commits(&self, num_shares: usize) -> ShareCommits {
51-
ShareCommits(
52-
self.shares(num_shares)
53-
.into_iter()
54-
.map(|(_, share)| Projective::generator() * share)
55-
.collect(),
56-
)
100+
pub fn share_commits(&self, secp: &Secp256k1, num_shares: usize) -> ShareCommits {
101+
let shares = self
102+
.shares(num_shares)
103+
.into_iter()
104+
.map(|(_, share)| share)
105+
.collect::<Vec<_>>();
106+
let commits = secp.generator_batch_mul(&shares);
107+
ShareCommits(commits)
57108
}
58109
}
59110

@@ -75,20 +126,22 @@ impl ShareCommits {
75126
Ok(())
76127
}
77128

78-
pub fn verify_shares(&self, shares: &[(usize, Fr)]) -> Result<(), String> {
129+
pub fn verify_shares(&self, secp: &Secp256k1, shares: &[(usize, Fr)]) -> Result<(), String> {
79130
let mut indices = shares.iter().map(|(i, _)| *i).collect::<Vec<_>>();
80131
indices.sort_unstable();
81132
if indices.windows(2).any(|arr| arr[0] == arr[1]) {
82133
return Err("Duplicate share index found".to_owned());
83134
}
84135

85-
for (i, share) in shares.iter() {
136+
let (indices, shares): (Vec<_>, Vec<_>) = shares.iter().copied().unzip();
137+
let recomputed_commits = secp.generator_batch_mul(&shares);
138+
for (index, recomputed_commit) in indices.iter().zip(recomputed_commits.into_iter()) {
86139
let share_commit = self
87140
.0
88-
.get(*i)
141+
.get(*index)
89142
.ok_or("Share index out of bounds".to_owned())?;
90143

91-
if *share_commit != Projective::generator() * share {
144+
if *share_commit != recomputed_commit {
92145
return Err("Share verification failed".to_owned());
93146
}
94147
}
@@ -191,6 +244,10 @@ pub fn lagrange_interpolate_whole_polynomial(
191244

192245
#[cfg(test)]
193246
mod tests {
247+
use std::time::Instant;
248+
249+
use ark_ec::ScalarMul;
250+
194251
use super::*;
195252
use rand::{SeedableRng, seq::index::sample};
196253
use rand_chacha::ChaCha20Rng;
@@ -211,16 +268,48 @@ mod tests {
211268
#[test]
212269
fn test_commit_verification() {
213270
let polynomial_degree = 3;
271+
272+
let secp = Secp256k1::new();
273+
214274
let polynomial = Polynomial::rand(rand::thread_rng(), polynomial_degree);
215275

216276
let num_shares = polynomial_degree + 1;
217-
let poly_commits = polynomial.coefficient_commits();
218-
let share_commits = polynomial.share_commits(num_shares);
277+
let poly_commits = polynomial.coefficient_commits(&secp);
278+
let share_commits = polynomial.share_commits(&secp, num_shares);
219279

220280
share_commits.verify(&poly_commits).unwrap();
221281

222282
let shares = polynomial.shares(num_shares);
223-
share_commits.verify_shares(&shares).unwrap();
283+
share_commits.verify_shares(&secp, &shares).unwrap();
284+
}
285+
286+
#[test]
287+
fn test_batch_mul() {
288+
let time_start = Instant::now();
289+
let secp = Secp256k1::new();
290+
println!(
291+
"secp256k1 precalculation time: {:?}",
292+
Instant::now() - time_start
293+
);
294+
295+
let approx_size = secp.generator.table.iter().map(|x| x.len()).sum::<usize>()
296+
* std::mem::size_of::<<Projective as ScalarMul>::MulBase>();
297+
println!("approx_size: {:?}", approx_size);
298+
299+
let coeffs = (0..174)
300+
.map(|_| Fr::rand(&mut rand::thread_rng()))
301+
.collect::<Vec<_>>();
302+
303+
let time_start = Instant::now();
304+
let vals_batched = secp.generator_batch_mul(&coeffs);
305+
println!("batch_mul time: {:?}", Instant::now() - time_start);
306+
307+
let generator = Projective::generator();
308+
let time_start = Instant::now();
309+
let vals_unbatched = coeffs.iter().map(|c| generator * c).collect::<Vec<_>>();
310+
println!("unbatched time: {:?}", Instant::now() - time_start);
311+
312+
assert_eq!(vals_batched, vals_unbatched);
224313
}
225314

226315
#[test]

0 commit comments

Comments
 (0)