Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve windowed scalar mul and auxiliary generator #81

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
3 changes: 2 additions & 1 deletion ecc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ subtle = { version = "2.3", default-features = false }
[dev-dependencies]
rand_core = { version = "0.6", default-features = false }
paste = "1.0.7"
rand_chacha = "0.3.1"

[features]
default = []
circuit-params = ["integer/circuit-params"]
circuit-params = ["integer/circuit-params"]
45 changes: 45 additions & 0 deletions ecc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
### Windowed scalar mul using auxiliary generator

$$
\begin{align}
0 &~~~~~~~ 1: ~~~~~~~~~~ [2]P ~~ [3]P ~~ \cdots~\cdots ~~~ [2^w +1]P \\
1 &~~~~~~ 2^w: ~~~~~~~~~ [2]P ~~ [3]P ~~ \cdots~\cdots ~~~ [2^w +1]P \\
2 &~~~~ (2^w)^2: ~~~~~~ [2]P ~~ [3]P ~~ \cdots~\cdots ~~~ [2^w +1]P \\
\cdots & \cdots \\
n-3 & ~~~~ (2^w)^{n-3}: ~~ [2]P ~~ [3]P ~~ \cdots~\cdots ~~~ [2^w +1]P \\
n-2 & ~~~~ (2^w)^{n-2}: ~~ [2]P ~~ [3]P ~~ \cdots~\cdots ~~~ [2^w +1]P \\
n-1 & ~~~~ (2^w)^{n-1}: ~~ [2]P ~~ [3]P ~~ \cdots ~~~ [2^\ell +1]P \\
\end{align}
$$

where window_size $w>1$ and scalar_size $= w(n-1) + \ell$ with $1\leq \ell \leq w$.

The scalar $k\in F_r$ can be adjusted upfront $k' = k - (2*\sum_{0\leq j\leq n-1} 2^{wj}) \mod r$ to avoid computing
correction point $[\sum_{0\leq j\leq n-1}2* 2^{wj}]P$. This works for both base_field_chip and general_ecc_chip.

The accumulation $acc_i$ is computed from the bottom up:

$$
\begin{align}
acc_{n-1} & = Q_{n-1} \\
acc_{n-2} & = 2^w acc_{n-1} + Q_{n-2} \\
acc_i & = 2^w acc_{i+1} + Q_i \\
& = 2(2^{w-1} acc_{i+1}) + Q_i \text{ for } i = n-3,...,2
\end{align}
$$

The scalar in $acc_{n-1},\dots, acc_2$ increases monotonically, and $acc_{n-3}...acc_2$ can be computed using laddr_incomplete.
The last two steps $acc_{1}, acc_0$ might overflow (when $\ell = 1$ and $w = 2$)
and need to use auxiliary generator and addition with assertions to ensure the x-coordinates are not the same:

$$
\begin{align}
acc_1 & = (2^{w} acc_2 + aux) + Q_1\\
acc_0 & = (2^w acc_1 + Q_0) - 2^w aux
\end{align}
$$

### mul_batch_1d_horizontal
This algorithm uses addtion with assertions in all steps and the auxiliary generator in the last two steps. It is only suitable for computing
$e_1 P_1 + e_2 P_2 + \cdots + e_n P_n$, where $P_1, \dots, P_n$ are randomly chosen,
i.e., their discrete logarithms are unknown. The algorithm is not suitable for computing things like $eP + sP$.
108 changes: 76 additions & 32 deletions ecc/src/base_field_ecc.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use super::{make_mul_aux, AssignedPoint, EccConfig, MulAux, Point};
use super::{AssignedPoint, EccConfig, MulAux, Point};
use crate::halo2::arithmetic::Field;
use crate::halo2::halo2curves::ff::PrimeField;
use crate::integer::chip::IntegerChip;
use crate::integer::rns::{Integer, Rns};
use crate::{halo2, maingate};
Expand Down Expand Up @@ -29,10 +31,14 @@ pub struct BaseFieldEccChip<C: CurveAffine, const NUMBER_OF_LIMBS: usize, const
AssignedPoint<C::Base, C::Scalar, NUMBER_OF_LIMBS, BIT_LEN_LIMB>,
Value<C>,
)>,
/// Auxiliary points for optimized multiplication for each (window_size,
/// n_pairs) pairs
aux_registry:
BTreeMap<(usize, usize), AssignedPoint<C::Base, C::Scalar, NUMBER_OF_LIMBS, BIT_LEN_LIMB>>,
/// Auxiliary points for optimized multiplication for each window_size
aux_registry: BTreeMap<
usize,
(
C::Scalar,
AssignedPoint<C::Base, C::Scalar, NUMBER_OF_LIMBS, BIT_LEN_LIMB>,
),
>,
}

impl<C: CurveAffine, const NUMBER_OF_LIMBS: usize, const BIT_LEN_LIMB: usize>
Expand Down Expand Up @@ -84,22 +90,26 @@ impl<C: CurveAffine, const NUMBER_OF_LIMBS: usize, const BIT_LEN_LIMB: usize>
}

/// Auxilary point for optimized multiplication algorithm
fn get_mul_aux(
fn get_mul_correction(
&self,
window_size: usize,
number_of_pairs: usize,
) -> Result<MulAux<C::Base, C::Scalar, NUMBER_OF_LIMBS, BIT_LEN_LIMB>, Error> {
) -> Result<
(
C::Scalar,
MulAux<C::Base, C::Scalar, NUMBER_OF_LIMBS, BIT_LEN_LIMB>,
),
Error,
> {
let to_add = match self.aux_generator.clone() {
Some((assigned, _)) => Ok(assigned),
None => Err(Error::Synthesis),
}?;
let to_sub = match self.aux_registry.get(&(window_size, number_of_pairs)) {
let (scalar_correction, to_sub) = match self.aux_registry.get(&window_size) {
Some(aux) => Ok(aux.clone()),
None => Err(Error::Synthesis),
}?;
// to_add the equivalent of AuxInit and to_sub AuxFin
// see https://hackmd.io/ncuKqRXzR-Cw-Au2fGzsMg?view
Ok(MulAux::new(to_add, to_sub))

Ok((scalar_correction, MulAux::new(to_add, to_sub)))
}
}

Expand Down Expand Up @@ -175,27 +185,48 @@ impl<C: CurveAffine, const NUMBER_OF_LIMBS: usize, const BIT_LEN_LIMB: usize>
Ok(())
}

/// Assigns multiplication auxiliary point for a pair of (window_size,
/// n_pairs)
pub fn assign_aux(
/// Assigns scalar correction and multiplication auxiliary point for window_size
pub fn assign_correction(
&mut self,
ctx: &mut RegionCtx<'_, C::Scalar>,
window_size: usize,
number_of_pairs: usize,
) -> Result<(), Error> {
match self.aux_generator {
Some((_, point)) => {
let aux = point.map(|point| make_mul_aux(point, window_size, number_of_pairs));
let aux = self.assign_point(ctx, aux)?;
let scalar_correction = self.correct_scalar(window_size);

match &self.aux_generator {
Some((point, _)) => {
// compute correction point -2^w aux
let mut point_correction = self.double_n(ctx, point, window_size)?;
point_correction = self.neg(ctx, &point_correction)?;

self.aux_registry
.insert((window_size, number_of_pairs), aux);
.insert(window_size, (scalar_correction, point_correction));
Ok(())
}
// aux generator is not assigned yet
None => Err(Error::Synthesis),
}
}

/// correct scalar before mul; correction value is -2(1 + 2^w + ... + 2^{w(n-1)})
fn correct_scalar(&mut self, window_size: usize) -> C::Scalar {
let window: usize = 1 << window_size;
let window_scalar = C::Scalar::from(window as u64);

let num_bits = C::Scalar::NUM_BITS as usize;
let number_of_windows = (num_bits + window_size - 1) / window_size;

let mut correction = C::Scalar::ONE;
let mut power = window_scalar;
for _ in 0..number_of_windows - 1 {
correction += power;
power *= window_scalar;
}
correction += correction;

-correction
}

/// Constraints to ensure `AssignedPoint` is on curve
pub fn assert_is_on_curve(
&self,
Expand Down Expand Up @@ -351,6 +382,7 @@ mod tests {
use crate::integer::rns::Rns;
use crate::integer::NUMBER_OF_LOOKUP_LIMBS;
use crate::maingate;
use crate::maingate::DimensionMeasurement;
use halo2::arithmetic::CurveAffine;
use halo2::circuit::{Layouter, SimpleFloorPlanner, Value};
use halo2::halo2curves::{
Expand All @@ -365,7 +397,8 @@ mod tests {
RangeInstructions,
};
use paste::paste;
use rand_core::OsRng;
use rand_chacha::ChaCha20Rng;
use rand_core::{OsRng, SeedableRng};

const NUMBER_OF_LIMBS: usize = 4;
const BIT_LEN_LIMB: usize = 68;
Expand Down Expand Up @@ -659,8 +692,7 @@ mod tests {
let offset = 0;
let ctx = &mut RegionCtx::new(region, offset);
ecc_chip.assign_aux_generator(ctx, Value::known(self.aux_generator))?;
ecc_chip.assign_aux(ctx, self.window_size, 1)?;
ecc_chip.get_mul_aux(self.window_size, 1)?;
ecc_chip.assign_correction(ctx, self.window_size)?;
Ok(())
},
)?;
Expand All @@ -671,8 +703,11 @@ mod tests {
let offset = 0;
let ctx = &mut RegionCtx::new(region, offset);

let base = C::CurveExt::random(OsRng);
let s = C::Scalar::random(OsRng);
// let mut rng = ChaCha20Rng::seed_from_u64(80);
let mut rng = OsRng;

let base = C::CurveExt::random(&mut rng);
let s = C::Scalar::random(&mut rng);
let result = base * s;

let base = ecc_chip.assign_point(ctx, Value::known(base.into()))?;
Expand All @@ -698,15 +733,23 @@ mod tests {
where
C::Scalar: FromUniformBytes<64>,
{
for window_size in 1..5 {
let aux_generator = <C as CurveAffine>::CurveExt::random(OsRng).to_affine();
//let mut rng = ChaCha20Rng::seed_from_u64(42);
let mut rng = OsRng;

for window_size in 2..5 {
let aux_generator = <C as CurveAffine>::CurveExt::random(&mut rng).to_affine();

let circuit = TestEccMul {
aux_generator,
window_size,
};
let instance = vec![vec![]];
mock_prover_verify(&circuit, instance);
let dimension = DimensionMeasurement::measure(&circuit).unwrap();
println!(
"window_size = {:?}, dimention: {:?}",
window_size, dimension
);
}
}
run::<Bn256>();
Expand Down Expand Up @@ -752,8 +795,7 @@ mod tests {
let offset = 0;
let ctx = &mut RegionCtx::new(region, offset);
ecc_chip.assign_aux_generator(ctx, Value::known(self.aux_generator))?;
ecc_chip.assign_aux(ctx, self.window_size, self.number_of_pairs)?;
ecc_chip.get_mul_aux(self.window_size, self.number_of_pairs)?;
ecc_chip.assign_correction(ctx, self.window_size)?;
Ok(())
},
)?;
Expand Down Expand Up @@ -799,8 +841,8 @@ mod tests {
paste! {
#[test]
fn [<test_base_field_ecc_mul_batch_circuit_ $C:lower>]() {
for number_of_pairs in 5..7 {
for window_size in 1..3 {
for number_of_pairs in 2..7 {
for window_size in 2..4 {
let aux_generator = <$C as CurveAffine>::CurveExt::random(OsRng).to_affine();

let circuit = TestEccBatchMul {
Expand All @@ -810,6 +852,8 @@ mod tests {
};
let instance = vec![vec![]];
mock_prover_verify(&circuit, instance);
let dimension = DimensionMeasurement::measure(&circuit).unwrap();
println!("(number of pairs, window_size) = ({:?}, {:?}), dimention: {:?}", number_of_pairs, window_size, dimension);
}
}
}
Expand Down
Loading
Loading