mirror of https://github.com/zcash/orchard.git
chip::mul.rs: Enforce LSB if/else condition
On the LSB of the scalar, we assign a point (x,y) = (x_p, -y_p) if LSB = 0, and (0,0) otherwise. This if/else condition must be enforced. Co-authored-by: Sean Bowe <ewillbefull@gmail.com>
This commit is contained in:
parent
e726fee19b
commit
69d6629ac6
|
@ -91,12 +91,12 @@ pub struct EccConfig {
|
||||||
pub q_mul_hi: Selector,
|
pub q_mul_hi: Selector,
|
||||||
/// Variable-base scalar multiplication (lo half)
|
/// Variable-base scalar multiplication (lo half)
|
||||||
pub q_mul_lo: Selector,
|
pub q_mul_lo: Selector,
|
||||||
/// Selector used in scalar decomposition for variable-base scalar mul
|
/// Selector used to initialize running sum to zero in variable-base scalar mul
|
||||||
pub q_mul_decompose_var: Selector,
|
|
||||||
/// Selector used in scalar decomposition for variable-base scalar mul
|
|
||||||
pub q_init_z: Selector,
|
pub q_init_z: Selector,
|
||||||
/// Variable-base scalar multiplication (final scalar)
|
/// Selector used to enforce boolean decomposition in variable-base scalar mul
|
||||||
pub q_mul_z: Selector,
|
pub q_mul_decompose_var: Selector,
|
||||||
|
/// Selector used to enforce switching logic on LSB in variable-base scalar mul
|
||||||
|
pub q_mul_lsb: Selector,
|
||||||
/// Variable-base scalar multiplication (overflow check)
|
/// Variable-base scalar multiplication (overflow check)
|
||||||
pub q_mul_overflow: Selector,
|
pub q_mul_overflow: Selector,
|
||||||
|
|
||||||
|
@ -171,10 +171,10 @@ impl EccChip {
|
||||||
q_add: meta.selector(),
|
q_add: meta.selector(),
|
||||||
q_mul_hi: meta.selector(),
|
q_mul_hi: meta.selector(),
|
||||||
q_mul_lo: meta.selector(),
|
q_mul_lo: meta.selector(),
|
||||||
q_mul_decompose_var: meta.selector(),
|
|
||||||
q_init_z: meta.selector(),
|
q_init_z: meta.selector(),
|
||||||
q_mul_z: meta.selector(),
|
q_mul_decompose_var: meta.selector(),
|
||||||
q_mul_overflow: meta.selector(),
|
q_mul_overflow: meta.selector(),
|
||||||
|
q_mul_lsb: meta.selector(),
|
||||||
mul_fixed: meta.fixed_column(),
|
mul_fixed: meta.fixed_column(),
|
||||||
q_mul_fixed_short: meta.selector(),
|
q_mul_fixed_short: meta.selector(),
|
||||||
base_field_fixed: meta.fixed_column(),
|
base_field_fixed: meta.fixed_column(),
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use super::{add, CellValue, EccConfig, EccPoint, EccScalarVar, Var};
|
use super::{add, CellValue, EccConfig, EccPoint, EccScalarVar, Var};
|
||||||
use crate::constants::{NUM_COMPLETE_BITS, T_Q};
|
use crate::{
|
||||||
|
circuit::gadget::utilities::copy,
|
||||||
|
constants::{NUM_COMPLETE_BITS, T_Q},
|
||||||
|
};
|
||||||
use std::ops::{Deref, Range};
|
use std::ops::{Deref, Range};
|
||||||
|
|
||||||
use bigint::U256;
|
use bigint::U256;
|
||||||
|
@ -7,7 +10,7 @@ use ff::PrimeField;
|
||||||
use halo2::{
|
use halo2::{
|
||||||
arithmetic::FieldExt,
|
arithmetic::FieldExt,
|
||||||
circuit::{Layouter, Region},
|
circuit::{Layouter, Region},
|
||||||
plonk::{ConstraintSystem, Error, Permutation, Selector},
|
plonk::{ConstraintSystem, Error, Expression, Permutation, Selector},
|
||||||
poly::Rotation,
|
poly::Rotation,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -36,8 +39,10 @@ const COMPLETE_RANGE: Range<usize> = INCOMPLETE_LEN..(INCOMPLETE_LEN + NUM_COMPL
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
// Selector used to constrain the initialization of the running sum to be zero.
|
// Selector used to constrain the initialization of the running sum to be zero.
|
||||||
q_init_z: Selector,
|
q_init_z: Selector,
|
||||||
// Selector used to check z_0 = 2*z_1 + k_0
|
// Selector used to check z_i = 2*z_{i+1} + k_i
|
||||||
q_mul_z: Selector,
|
q_mul_decompose_var: Selector,
|
||||||
|
// Selector used to check switching logic on LSB
|
||||||
|
q_mul_lsb: Selector,
|
||||||
// Permutation
|
// Permutation
|
||||||
perm: Permutation,
|
perm: Permutation,
|
||||||
// Configuration used in complete addition
|
// Configuration used in complete addition
|
||||||
|
@ -56,7 +61,8 @@ impl From<&EccConfig> for Config {
|
||||||
fn from(ecc_config: &EccConfig) -> Self {
|
fn from(ecc_config: &EccConfig) -> Self {
|
||||||
let config = Self {
|
let config = Self {
|
||||||
q_init_z: ecc_config.q_init_z,
|
q_init_z: ecc_config.q_init_z,
|
||||||
q_mul_z: ecc_config.q_mul_z,
|
q_mul_decompose_var: ecc_config.q_mul_decompose_var,
|
||||||
|
q_mul_lsb: ecc_config.q_mul_lsb,
|
||||||
perm: ecc_config.perm.clone(),
|
perm: ecc_config.perm.clone(),
|
||||||
add_config: ecc_config.into(),
|
add_config: ecc_config.into(),
|
||||||
hi_config: ecc_config.into(),
|
hi_config: ecc_config.into(),
|
||||||
|
@ -79,6 +85,10 @@ impl From<&EccConfig> for Config {
|
||||||
!add_config_advices.contains(&config.hi_config.z),
|
!add_config_advices.contains(&config.hi_config.z),
|
||||||
"hi_config z cannot overlap with complete addition columns."
|
"hi_config z cannot overlap with complete addition columns."
|
||||||
);
|
);
|
||||||
|
assert!(
|
||||||
|
!add_config_advices.contains(&config.complete_config.z_complete),
|
||||||
|
"complete_config z cannot overlap with complete addition columns."
|
||||||
|
);
|
||||||
|
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
@ -94,6 +104,35 @@ impl Config {
|
||||||
vec![q_init_z * z]
|
vec![q_init_z * z]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// If `lsb` is 0, (x, y) = (x_p, -y_p). If `lsb` is 1, (x, y) = (0,0).
|
||||||
|
meta.create_gate("LSB check", |meta| {
|
||||||
|
let q_mul_lsb = meta.query_selector(self.q_mul_lsb);
|
||||||
|
|
||||||
|
let z_1 = meta.query_advice(self.complete_config.z_complete, Rotation::prev());
|
||||||
|
let z_0 = meta.query_advice(self.complete_config.z_complete, Rotation::cur());
|
||||||
|
let x_p = meta.query_advice(self.add_config.x_p, Rotation::next());
|
||||||
|
let y_p = meta.query_advice(self.add_config.y_p, Rotation::next());
|
||||||
|
let x = meta.query_advice(self.add_config.x_qr, Rotation::next());
|
||||||
|
let y = meta.query_advice(self.add_config.y_qr, Rotation::next());
|
||||||
|
|
||||||
|
// z_0 = 2 * z_1 + k_0
|
||||||
|
// => k_0 = z_0 - 2 * z_1
|
||||||
|
let lsb = z_0 - z_1 * pallas::Base::from_u64(2);
|
||||||
|
|
||||||
|
let one = Expression::Constant(pallas::Base::one());
|
||||||
|
|
||||||
|
// `lsb` = 0 => (x, y) = (x_p, -y_p)
|
||||||
|
let lsb0_x = (one.clone() - lsb.clone()) * (x.clone() - x_p);
|
||||||
|
let lsb0_y = (one - lsb.clone()) * (y.clone() + y_p);
|
||||||
|
|
||||||
|
// `lsb` = 1 => (x, y) = (0,0)
|
||||||
|
let lsb1_x = lsb.clone() * x;
|
||||||
|
let lsb1_y = lsb * y;
|
||||||
|
|
||||||
|
std::array::IntoIter::new([lsb0_x, lsb0_y, lsb1_x, lsb1_y])
|
||||||
|
.map(move |poly| q_mul_lsb.clone() * poly)
|
||||||
|
});
|
||||||
|
|
||||||
self.hi_config.create_gate(meta);
|
self.hi_config.create_gate(meta);
|
||||||
self.lo_config.create_gate(meta);
|
self.lo_config.create_gate(meta);
|
||||||
self.complete_config.create_gate(meta);
|
self.complete_config.create_gate(meta);
|
||||||
|
@ -245,6 +284,9 @@ impl Config {
|
||||||
z_1: Z<pallas::Base>,
|
z_1: Z<pallas::Base>,
|
||||||
lsb: Option<bool>,
|
lsb: Option<bool>,
|
||||||
) -> Result<(EccPoint, Z<pallas::Base>), Error> {
|
) -> Result<(EccPoint, Z<pallas::Base>), Error> {
|
||||||
|
// Enforce switching logic on LSB using a custom gate
|
||||||
|
self.q_mul_lsb.enable(region, offset)?;
|
||||||
|
|
||||||
// Assign z_0 = 2⋅z_1 + k_0
|
// Assign z_0 = 2⋅z_1 + k_0
|
||||||
let z_0 = {
|
let z_0 = {
|
||||||
let z_0_val = z_1.value().zip(lsb).map(|(z_1, lsb)| {
|
let z_0_val = z_1.value().zip(lsb).map(|(z_1, lsb)| {
|
||||||
|
@ -259,13 +301,33 @@ impl Config {
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Check that z_0 was properly derived from z_1.
|
// Check that z_0 was properly derived from z_1.
|
||||||
self.q_mul_z.enable(region, offset)?;
|
self.q_mul_decompose_var.enable(region, offset)?;
|
||||||
|
|
||||||
Z(CellValue::new(z_0_cell, z_0_val))
|
Z(CellValue::new(z_0_cell, z_0_val))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let offset = offset + 1;
|
||||||
|
|
||||||
|
// Copy in `x_p`, `y_p` to use in the LSB gate
|
||||||
|
copy(
|
||||||
|
region,
|
||||||
|
|| "copy x_p",
|
||||||
|
self.add_config.x_p,
|
||||||
|
offset,
|
||||||
|
&base.x(),
|
||||||
|
&self.perm,
|
||||||
|
)?;
|
||||||
|
copy(
|
||||||
|
region,
|
||||||
|
|| "copy y_p",
|
||||||
|
self.add_config.y_p,
|
||||||
|
offset,
|
||||||
|
&base.y(),
|
||||||
|
&self.perm,
|
||||||
|
)?;
|
||||||
|
|
||||||
// If `lsb` is 0, return `Acc + (-P)`. If `lsb` is 1, simply return `Acc + 0`.
|
// If `lsb` is 0, return `Acc + (-P)`. If `lsb` is 1, simply return `Acc + 0`.
|
||||||
let x_p = if let Some(lsb) = lsb {
|
let x = if let Some(lsb) = lsb {
|
||||||
if !lsb {
|
if !lsb {
|
||||||
base.x.value()
|
base.x.value()
|
||||||
} else {
|
} else {
|
||||||
|
@ -274,7 +336,8 @@ impl Config {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let y_p = if let Some(lsb) = lsb {
|
|
||||||
|
let y = if let Some(lsb) = lsb {
|
||||||
if !lsb {
|
if !lsb {
|
||||||
base.y.value().map(|y_p| -y_p)
|
base.y.value().map(|y_p| -y_p)
|
||||||
} else {
|
} else {
|
||||||
|
@ -284,25 +347,27 @@ impl Config {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let x_p_cell = region.assign_advice(
|
let x_cell = region.assign_advice(
|
||||||
|| "x_p",
|
|| "x",
|
||||||
self.add_config.x_p,
|
self.add_config.x_qr,
|
||||||
offset,
|
offset,
|
||||||
|| x_p.ok_or(Error::SynthesisError),
|
|| x.ok_or(Error::SynthesisError),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let y_p_cell = region.assign_advice(
|
let y_cell = region.assign_advice(
|
||||||
|| "y_p",
|
|| "y",
|
||||||
self.add_config.y_p,
|
self.add_config.y_qr,
|
||||||
offset,
|
offset,
|
||||||
|| y_p.ok_or(Error::SynthesisError),
|
|| y.ok_or(Error::SynthesisError),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let p = EccPoint {
|
let p = EccPoint {
|
||||||
x: CellValue::<pallas::Base>::new(x_p_cell, x_p),
|
x: CellValue::<pallas::Base>::new(x_cell, x),
|
||||||
y: CellValue::<pallas::Base>::new(y_p_cell, y_p),
|
y: CellValue::<pallas::Base>::new(y_cell, y),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let offset = offset + 1;
|
||||||
|
|
||||||
// Return the result of the final complete addition as `[scalar]B`
|
// Return the result of the final complete addition as `[scalar]B`
|
||||||
let result = self.add_config.assign_region(&p, &acc, offset, region)?;
|
let result = self.add_config.assign_region(&p, &acc, offset, region)?;
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ use pasta_curves::{arithmetic::FieldExt, pallas};
|
||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
// Selector used to constrain the cells used in complete addition.
|
// Selector used to constrain the cells used in complete addition.
|
||||||
q_mul_z: Selector,
|
q_mul_decompose_var: Selector,
|
||||||
// Advice column used to decompose scalar in complete addition.
|
// Advice column used to decompose scalar in complete addition.
|
||||||
pub z_complete: Column<Advice>,
|
pub z_complete: Column<Advice>,
|
||||||
// Permutation
|
// Permutation
|
||||||
|
@ -23,7 +23,7 @@ pub struct Config {
|
||||||
impl From<&EccConfig> for Config {
|
impl From<&EccConfig> for Config {
|
||||||
fn from(ecc_config: &EccConfig) -> Self {
|
fn from(ecc_config: &EccConfig) -> Self {
|
||||||
let config = Self {
|
let config = Self {
|
||||||
q_mul_z: ecc_config.q_mul_z,
|
q_mul_decompose_var: ecc_config.q_mul_decompose_var,
|
||||||
z_complete: ecc_config.advices[9],
|
z_complete: ecc_config.advices[9],
|
||||||
perm: ecc_config.perm.clone(),
|
perm: ecc_config.perm.clone(),
|
||||||
add_config: ecc_config.into(),
|
add_config: ecc_config.into(),
|
||||||
|
@ -48,7 +48,7 @@ impl Config {
|
||||||
meta.create_gate(
|
meta.create_gate(
|
||||||
"Decompose scalar for complete bits of variable-base mul",
|
"Decompose scalar for complete bits of variable-base mul",
|
||||||
|meta| {
|
|meta| {
|
||||||
let q_mul_z = meta.query_selector(self.q_mul_z);
|
let q_mul_decompose_var = meta.query_selector(self.q_mul_decompose_var);
|
||||||
let z_cur = meta.query_advice(self.z_complete, Rotation::cur());
|
let z_cur = meta.query_advice(self.z_complete, Rotation::cur());
|
||||||
let z_prev = meta.query_advice(self.z_complete, Rotation::prev());
|
let z_prev = meta.query_advice(self.z_complete, Rotation::prev());
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ impl Config {
|
||||||
// (k_i) ⋅ (k_i - 1) = 0
|
// (k_i) ⋅ (k_i - 1) = 0
|
||||||
let bool_check = k.clone() * (k + Expression::Constant(-pallas::Base::one()));
|
let bool_check = k.clone() * (k + Expression::Constant(-pallas::Base::one()));
|
||||||
|
|
||||||
vec![q_mul_z * bool_check]
|
vec![q_mul_decompose_var * bool_check]
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -84,9 +84,9 @@ impl Config {
|
||||||
// Each iteration uses 2 rows (two complete additions)
|
// Each iteration uses 2 rows (two complete additions)
|
||||||
let row = 2 * row;
|
let row = 2 * row;
|
||||||
// Check scalar decomposition for each iteration. Since the gate enabled by
|
// Check scalar decomposition for each iteration. Since the gate enabled by
|
||||||
// `q_mul_z` queries the previous row, we enable the selector on
|
// `q_mul_decompose_var` queries the previous row, we enable the selector on
|
||||||
// `row + offset + 1` (instead of `row + offset`).
|
// `row + offset + 1` (instead of `row + offset`).
|
||||||
self.q_mul_z.enable(region, row + offset + 1)?;
|
self.q_mul_decompose_var.enable(region, row + offset + 1)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use x_a, y_a output from incomplete addition
|
// Use x_a, y_a output from incomplete addition
|
||||||
|
|
|
@ -17,7 +17,7 @@ use std::iter;
|
||||||
|
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
// Selector to check decomposition of lsb
|
// Selector to check decomposition of lsb
|
||||||
q_mul_z: Selector,
|
q_mul_decompose_var: Selector,
|
||||||
// Selector to check z_0 = alpha + t_q (mod p)
|
// Selector to check z_0 = alpha + t_q (mod p)
|
||||||
q_mul_overflow: Selector,
|
q_mul_overflow: Selector,
|
||||||
// 10-bit lookup table
|
// 10-bit lookup table
|
||||||
|
@ -31,7 +31,7 @@ pub struct Config {
|
||||||
impl From<&EccConfig> for Config {
|
impl From<&EccConfig> for Config {
|
||||||
fn from(ecc_config: &EccConfig) -> Self {
|
fn from(ecc_config: &EccConfig) -> Self {
|
||||||
Self {
|
Self {
|
||||||
q_mul_z: ecc_config.q_mul_z,
|
q_mul_decompose_var: ecc_config.q_mul_decompose_var,
|
||||||
q_mul_overflow: ecc_config.q_mul_overflow,
|
q_mul_overflow: ecc_config.q_mul_overflow,
|
||||||
lookup_config: ecc_config.lookup_config.clone(),
|
lookup_config: ecc_config.lookup_config.clone(),
|
||||||
advices: [
|
advices: [
|
||||||
|
|
|
@ -33,7 +33,7 @@ impl Config {
|
||||||
let one = Expression::Constant(pallas::Base::one());
|
let one = Expression::Constant(pallas::Base::one());
|
||||||
|
|
||||||
let last_window_check = last_window.clone() * (one.clone() - last_window);
|
let last_window_check = last_window.clone() * (one.clone() - last_window);
|
||||||
let sign_check = (one.clone() + sign.clone()) * (one - sign);
|
let sign_check = sign.clone() * sign - one;
|
||||||
|
|
||||||
vec![
|
vec![
|
||||||
q_scalar_fixed_short.clone() * last_window_check,
|
q_scalar_fixed_short.clone() * last_window_check,
|
||||||
|
|
Loading…
Reference in New Issue