Skip to content
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
11 changes: 11 additions & 0 deletions crates/halo2_gadgets/benches/primitives.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use ff::Field;
use group::{Curve, Group};
use halo2_gadgets::{
ecc::chip::{NUM_WINDOWS, compute_lagrange_coeffs},
poseidon::primitives::{self as poseidon, ConstantLength, P128Pow5T3},
sinsemilla::primitives as sinsemilla,
};
Expand Down Expand Up @@ -58,6 +60,15 @@ fn bench_primitives(c: &mut Criterion) {
});
}
}

{
let mut group = c.benchmark_group("ECC fixed-base preprocessing");
group.sample_size(10);
let base = pallas::Point::generator().to_affine();
group.bench_function("full-width Lagrange coefficients", |b| {
b.iter(|| compute_lagrange_coeffs(base, NUM_WINDOWS))
});
}
}

#[cfg(unix)]
Expand Down
21 changes: 12 additions & 9 deletions crates/halo2_gadgets/src/ecc/chip/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,38 +39,41 @@ pub(crate) const T_P: u128 = 45560315531419706090280762371685220353;
/// [the Halo 2 book](https://zcash.github.io/halo2/design/gadgets/ecc/fixed-base-scalar-mul.html#load-fixed-base).
fn compute_window_table<C: CurveAffine>(base: C, num_windows: usize) -> Vec<[C; H]> {
let mut window_table: Vec<[C; H]> = Vec::with_capacity(num_windows);
let h = C::Scalar::from(H as u64);
let mut window_scale = C::Scalar::ONE;

// Generate window table entries for all windows but the last.
// For these first `num_windows - 1` windows, we compute the multiple [(k+2)*(2^3)^w]B.
// Here, w ranges from [0..`num_windows - 1`)
for w in 0..(num_windows - 1) {
for _ in 0..(num_windows - 1) {
window_table.push(
(0..H)
.map(|k| {
// scalar = (k+2)*(8^w)
let scalar = C::Scalar::from(k as u64 + 2)
* C::Scalar::from(H as u64).pow([w as u64, 0, 0, 0]);
let scalar = C::Scalar::from(k as u64 + 2) * window_scale;
(base * scalar).to_affine()
})
.collect::<ArrayVec<C, H>>()
.into_inner()
.unwrap(),
);
window_scale *= h;
}

// Generate window table entries for the last window, w = `num_windows - 1`.
// For the last window, we compute [k * (2^3)^w - sum]B, where sum is defined
// as sum = \sum_{j = 0}^{`num_windows - 2`} 2^{3j+1}
let sum = (0..(num_windows - 1)).fold(C::Scalar::ZERO, |acc, j| {
acc + C::Scalar::from(2).pow([FIXED_BASE_WINDOW_SIZE as u64 * j as u64 + 1, 0, 0, 0])
});
let mut offset = C::Scalar::from(2);
let mut sum = C::Scalar::ZERO;
for _ in 0..(num_windows - 1) {
sum += offset;
offset *= h;
}
window_table.push(
(0..H)
.map(|k| {
// scalar = k * (2^3)^w - sum, where w = `num_windows - 1`
let scalar = C::Scalar::from(k as u64)
* C::Scalar::from(H as u64).pow([(num_windows - 1) as u64, 0, 0, 0])
- sum;
let scalar = C::Scalar::from(k as u64) * window_scale - sum;
(base * scalar).to_affine()
})
.collect::<ArrayVec<C, H>>()
Expand Down
53 changes: 52 additions & 1 deletion crates/halo2_proofs/src/plonk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,65 @@ pub use keygen::*;
pub use prover::*;
pub use verifier::*;

use std::{io, sync::Arc};
use std::{
any::{Any as StdAny, TypeId},
io,
sync::Arc,
};

fn commit_instance<C: CurveAffine>(params: &Params<C>, instance: &[C::Scalar]) -> C::Curve {
let mut commitment = C::Curve::from(params.w);
commitment += best_multiexp::<C>(instance, &params.g_lagrange[..instance.len()]);
commitment
}

/// Computes `base^(2^exponent)` using the public exponent directly.
fn pow_by_power_of_two<F: Field + 'static>(base: F, exponent: u32) -> F {
if TypeId::of::<F>() == TypeId::of::<pasta_curves::Fp>() {
let value = (&base as &dyn StdAny)
.downcast_ref::<pasta_curves::Fp>()
.expect("the field type was checked");
let result = pasta_curves::arithmetic::square_fp_n(value, exponent);
return *(&result as &dyn StdAny)
.downcast_ref::<F>()
.expect("the field type was checked");
}
if TypeId::of::<F>() == TypeId::of::<pasta_curves::Fq>() {
let value = (&base as &dyn StdAny)
.downcast_ref::<pasta_curves::Fq>()
.expect("the field type was checked");
let result = pasta_curves::arithmetic::square_fq_n(value, exponent);
return *(&result as &dyn StdAny)
.downcast_ref::<F>()
.expect("the field type was checked");
}

let mut base = base;
for _ in 0..exponent {
base = base.square();
}
base
}

#[cfg(test)]
mod power_of_two_tests {
use super::pow_by_power_of_two;
use pasta_curves::{Fp, Fq};

#[test]
fn matches_repeated_field_squaring() {
let fp = Fp::from(0x9e37_79b9_7f4a_7c15);
let fq = Fq::from(0x0123_4567_89ab_cdef);

for exponent in [0, 1, 2, 11, 64] {
let expected_fp = (0..exponent).fold(fp, |value, _| value.square());
let expected_fq = (0..exponent).fold(fq, |value, _| value.square());
assert_eq!(pow_by_power_of_two(fp, exponent), expected_fp);
assert_eq!(pow_by_power_of_two(fq, exponent), expected_fq);
}
}
}

/// This is a verifying key which allows for the verification of proofs for a
/// particular circuit.
#[derive(Clone, Debug)]
Expand Down
2 changes: 1 addition & 1 deletion crates/halo2_proofs/src/plonk/prover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1295,7 +1295,7 @@ where
}

let x: ChallengeX<_> = transcript.squeeze_challenge_scalar();
let xn = x.pow([params.n, 0, 0, 0]);
let xn = super::pow_by_power_of_two(*x, params.k);
let polynomial_evaluator = PolynomialEvaluator::new(
[
*x,
Expand Down
2 changes: 1 addition & 1 deletion crates/halo2_proofs/src/plonk/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ fn verify_proof_with_instance_commitments<
// commitments open to the correct values.
let vanishing = {
// x^n
let xn = x.pow([params.n, 0, 0, 0]);
let xn = super::pow_by_power_of_two(*x, params.k);

let blinding_factors = vk.cs.blinding_factors();
let l_evals = vk
Expand Down
6 changes: 4 additions & 2 deletions crates/orchard/src/circuit/commit_ivk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

use core::iter;

use group::ff::{Field, PrimeField};
use group::ff::PrimeField;
use halo2_proofs::{
circuit::{AssignedCell, Layouter, Value},
plonk::{Advice, Column, ConstraintSystem, Constraints, Error, Expression, Selector},
Expand Down Expand Up @@ -130,7 +130,9 @@ impl CommitIvkChip {

// Check that nk = b_2 (5 bits) || c (240 bits) || d_0 (9 bits) || d_1 (1 bit)
let nk_decomposition_check = {
let two_pow_245 = pallas::Base::from(1 << 49).pow([5, 0, 0, 0]);
let two_pow_49 = pallas::Base::from(1 << 49);
let two_pow_245 =
pasta_curves::arithmetic::square_fp_n(&two_pow_49, 2) * two_pow_49;

b_2.clone()
+ c.clone() * two_pow_5
Expand Down
17 changes: 16 additions & 1 deletion crates/pasta_curves/benches/fp.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
///! Benchmarks for the Fp field.
use criterion::{Bencher, Criterion, criterion_group, criterion_main};
use criterion::{Bencher, Criterion, black_box, criterion_group, criterion_main};

use rand::SeedableRng;
use rand_xorshift::XorShiftRng;

use ff::{Field, PrimeField};
use pasta_curves::Fp;
use pasta_curves::arithmetic::square_fp_n;

const PROVER_DOMAIN_EXPONENT: u32 = 11;

fn criterion_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("Fp");
Expand All @@ -22,6 +25,18 @@ fn criterion_benchmark(c: &mut Criterion) {
group.bench_function("from_repr", bench_fp_from_repr);
group.bench_function("eq/equal", |b| bench_fp_eq(b, true));
group.bench_function("eq/unequal", |b| bench_fp_eq(b, false));
group.bench_function("pow 2^11/constant-time", bench_fp_pow_power_of_two);
group.bench_function("pow 2^11/repeated-squaring", bench_fp_square_n);
}

fn bench_fp_pow_power_of_two(b: &mut Bencher) {
let value = Fp::from(0x9e37_79b9_7f4a_7c15);
b.iter(|| black_box(value).pow([1 << PROVER_DOMAIN_EXPONENT, 0, 0, 0]));
}

fn bench_fp_square_n(b: &mut Bencher) {
let value = Fp::from(0x9e37_79b9_7f4a_7c15);
b.iter(|| square_fp_n(black_box(&value), PROVER_DOMAIN_EXPONENT));
}

fn bench_fp_eq(b: &mut Bencher, equal: bool) {
Expand Down
17 changes: 16 additions & 1 deletion crates/pasta_curves/benches/fq.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
///! Benchmarks for the Fq field.
use criterion::{Bencher, Criterion, criterion_group, criterion_main};
use criterion::{Bencher, Criterion, black_box, criterion_group, criterion_main};

use rand::SeedableRng;
use rand_xorshift::XorShiftRng;

use ff::{Field, PrimeField};
use pasta_curves::Fq;
use pasta_curves::arithmetic::square_fq_n;

const PROVER_DOMAIN_EXPONENT: u32 = 11;

fn criterion_benchmark(c: &mut Criterion) {
let mut group = c.benchmark_group("Fq");
Expand All @@ -22,6 +25,18 @@ fn criterion_benchmark(c: &mut Criterion) {
group.bench_function("from_repr", bench_fq_from_repr);
group.bench_function("eq/equal", |b| bench_fq_eq(b, true));
group.bench_function("eq/unequal", |b| bench_fq_eq(b, false));
group.bench_function("pow 2^11/constant-time", bench_fq_pow_power_of_two);
group.bench_function("pow 2^11/repeated-squaring", bench_fq_square_n);
}

fn bench_fq_pow_power_of_two(b: &mut Bencher) {
let value = Fq::from(0x9e37_79b9_7f4a_7c15);
b.iter(|| black_box(value).pow([1 << PROVER_DOMAIN_EXPONENT, 0, 0, 0]));
}

fn bench_fq_square_n(b: &mut Bencher) {
let value = Fq::from(0x9e37_79b9_7f4a_7c15);
b.iter(|| square_fq_n(black_box(&value), PROVER_DOMAIN_EXPONENT));
}

fn bench_fq_eq(b: &mut Bencher, equal: bool) {
Expand Down
45 changes: 45 additions & 0 deletions crates/pasta_curves/src/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,48 @@ pub fn mul_fp_by_inverse_power_of_two(value: &crate::Fp, exponent: u32) -> crate
pub fn mul_fq_by_inverse_power_of_two(value: &crate::Fq, exponent: u32) -> crate::Fq {
value.mul_by_inverse_power_of_two(exponent)
}

/// Squares an [`Fp`](crate::Fp) element `count` times.
///
/// This is an internal cross-crate bridge for Halo 2 and Orchard.
#[doc(hidden)]
#[inline]
pub fn square_fp_n(value: &crate::Fp, count: u32) -> crate::Fp {
if count == 0 {
*value
} else {
SqrtTableHelpers::sqr_n(value, count)
}
}

/// Squares an [`Fq`](crate::Fq) element `count` times.
///
/// This is an internal cross-crate bridge for Halo 2 and Orchard.
#[doc(hidden)]
#[inline]
pub fn square_fq_n(value: &crate::Fq, count: u32) -> crate::Fq {
if count == 0 {
*value
} else {
SqrtTableHelpers::sqr_n(value, count)
}
}

#[cfg(test)]
mod tests {
use super::{square_fp_n, square_fq_n};
use crate::{Fp, Fq};

#[test]
fn repeated_squaring_bridges_match_field_squaring() {
let fp = Fp::from(0x9e37_79b9_7f4a_7c15);
let fq = Fq::from(0x0123_4567_89ab_cdef);

for count in [0, 1, 2, 11, 64] {
let expected_fp = (0..count).fold(fp, |value, _| value.square());
let expected_fq = (0..count).fold(fq, |value, _| value.square());
assert_eq!(square_fp_n(&fp, count), expected_fp);
assert_eq!(square_fq_n(&fq, count), expected_fq);
}
}
}
29 changes: 29 additions & 0 deletions docs/changelog/unreleased/333.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
## zakura-pasta-curves

### Added

- Added the doc-hidden `arithmetic::{square_fp_n, square_fq_n}` cross-crate
bridges for invoking the dedicated repeated-squaring implementation
([#333](https://github.com/zakura-core/common/pull/333)).

## zakura-halo2-proofs

### Changed

- Changed the public-domain `x^(2^k)` calculation used by proof creation and
verification to call Pasta's dedicated repeated-squaring implementation.
At `k = 11`, the isolated calculation was 63.6x faster on Apple M4 and
64.9x faster on x86_64 Linux; proof bytes and transcripts are unchanged
([#333](https://github.com/zakura-core/common/pull/333)).

## zakura-halo2-gadgets

### Changed

- Changed generic fixed-base window preprocessing to advance public window
scales iteratively instead of recomputing each power with constant-time
exponentiation, reducing full-width Lagrange-coefficient generation by
14.7% on Apple M4 and 16.3% on x86_64 Linux. Orchard uses precomputed
coefficient tables, so these measurements do not represent an Orchard
key-generation speedup
([#333](https://github.com/zakura-core/common/pull/333)).
Loading