mirror of https://github.com/zcash/halo2.git
chip::witness_scalar_fixed.rs: Implement witness_scalar_fixed instruction.
A scalar used in fixed-base scalar mul needs to be decomposed into windows to use with the fixed-base window table. Both full-width and short signed scalars share some logic (captured in the function decompose_scalar_fixed()). A short signed scalar introduces additional logic: its magnitude is decomposed, and its sign is separately witnessed. This is handled in the submodule witness_scalar_fixed::short.
This commit is contained in:
parent
cc9dd20536
commit
a263774abf
|
@ -15,7 +15,7 @@ pub(super) mod add_incomplete;
|
||||||
pub(super) mod mul;
|
pub(super) mod mul;
|
||||||
// pub(super) mod mul_fixed;
|
// pub(super) mod mul_fixed;
|
||||||
pub(super) mod witness_point;
|
pub(super) mod witness_point;
|
||||||
// pub(super) mod witness_scalar_fixed;
|
pub(super) mod witness_scalar_fixed;
|
||||||
|
|
||||||
/// A curve point represented in affine (x, y) coordinates. Each coordinate is
|
/// A curve point represented in affine (x, y) coordinates. Each coordinate is
|
||||||
/// assigned to a cell.
|
/// assigned to a cell.
|
||||||
|
@ -186,6 +186,19 @@ impl EccChip {
|
||||||
mul_config.create_gate(meta);
|
mul_config.create_gate(meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create witness scalar_fixed gate that applies to both full-width and
|
||||||
|
// short scalars
|
||||||
|
{
|
||||||
|
let config: witness_scalar_fixed::Config = (&config).into();
|
||||||
|
config.create_gate(meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create witness scalar_fixed gate that only apploes to short scalars
|
||||||
|
{
|
||||||
|
let config: witness_scalar_fixed::short::Config = (&config).into();
|
||||||
|
config.create_gate(meta);
|
||||||
|
}
|
||||||
|
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,18 +275,26 @@ impl EccInstructions<pallas::Affine> for EccChip {
|
||||||
|
|
||||||
fn witness_scalar_fixed(
|
fn witness_scalar_fixed(
|
||||||
&self,
|
&self,
|
||||||
_layouter: &mut impl Layouter<pallas::Base>,
|
layouter: &mut impl Layouter<pallas::Base>,
|
||||||
_value: Option<pallas::Scalar>,
|
value: Option<pallas::Scalar>,
|
||||||
) -> Result<Self::ScalarFixed, Error> {
|
) -> Result<Self::ScalarFixed, Error> {
|
||||||
todo!()
|
let config: witness_scalar_fixed::full_width::Config = self.config().into();
|
||||||
|
layouter.assign_region(
|
||||||
|
|| "witness scalar for fixed-base mul",
|
||||||
|
|mut region| config.assign_region(value, 0, &mut region),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn witness_scalar_fixed_short(
|
fn witness_scalar_fixed_short(
|
||||||
&self,
|
&self,
|
||||||
_layouter: &mut impl Layouter<pallas::Base>,
|
layouter: &mut impl Layouter<pallas::Base>,
|
||||||
_value: Option<pallas::Scalar>,
|
value: Option<pallas::Scalar>,
|
||||||
) -> Result<Self::ScalarFixedShort, Error> {
|
) -> Result<Self::ScalarFixedShort, Error> {
|
||||||
todo!()
|
let config: witness_scalar_fixed::short::Config = self.config().into();
|
||||||
|
layouter.assign_region(
|
||||||
|
|| "witness short scalar for fixed-base mul",
|
||||||
|
|mut region| config.assign_region(value, 0, &mut region),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn witness_point(
|
fn witness_point(
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
use super::{CellValue, EccConfig, Var};
|
||||||
|
use crate::constants::{self, util};
|
||||||
|
use arrayvec::ArrayVec;
|
||||||
|
use ff::PrimeFieldBits;
|
||||||
|
use halo2::{
|
||||||
|
circuit::Region,
|
||||||
|
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector},
|
||||||
|
poly::Rotation,
|
||||||
|
};
|
||||||
|
use pasta_curves::{arithmetic::FieldExt, pallas};
|
||||||
|
|
||||||
|
pub mod full_width;
|
||||||
|
pub mod short;
|
||||||
|
|
||||||
|
pub struct Config {
|
||||||
|
q_scalar_fixed: Selector,
|
||||||
|
// Decomposition of scalar into `k`-bit windows.
|
||||||
|
window: Column<Advice>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&EccConfig> for Config {
|
||||||
|
fn from(ecc_config: &EccConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
q_scalar_fixed: ecc_config.q_scalar_fixed,
|
||||||
|
window: ecc_config.advices[0],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub(super) fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) {
|
||||||
|
// Range check gate applies to both full-width and short scalars.
|
||||||
|
// Check that `k` is within the allowed window size
|
||||||
|
meta.create_gate("witness scalar fixed", |meta| {
|
||||||
|
let q_scalar_fixed = meta.query_selector(self.q_scalar_fixed);
|
||||||
|
let window = meta.query_advice(self.window, Rotation::cur());
|
||||||
|
|
||||||
|
let range_check =
|
||||||
|
(0..constants::H).fold(Expression::Constant(pallas::Base::one()), |acc, i| {
|
||||||
|
acc * (window.clone() - Expression::Constant(pallas::Base::from_u64(i as u64)))
|
||||||
|
});
|
||||||
|
vec![q_scalar_fixed * range_check]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decompose_scalar_fixed<const NUM_WINDOWS: usize, const SCALAR_NUM_BITS: usize>(
|
||||||
|
&self,
|
||||||
|
scalar: Option<pallas::Scalar>,
|
||||||
|
offset: usize,
|
||||||
|
region: &mut Region<'_, pallas::Base>,
|
||||||
|
) -> Result<ArrayVec<CellValue<pallas::Base>, NUM_WINDOWS>, Error>
|
||||||
|
where
|
||||||
|
pallas::Scalar: PrimeFieldBits,
|
||||||
|
{
|
||||||
|
// Enable `q_scalar_fixed` selector
|
||||||
|
for idx in 0..NUM_WINDOWS {
|
||||||
|
self.q_scalar_fixed.enable(region, offset + idx)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decompose scalar into `k-bit` windows
|
||||||
|
let scalar_windows: Option<Vec<u8>> = scalar.map(|scalar| {
|
||||||
|
util::decompose_scalar_fixed::<pallas::Scalar>(
|
||||||
|
scalar,
|
||||||
|
SCALAR_NUM_BITS,
|
||||||
|
constants::FIXED_BASE_WINDOW_SIZE,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Store the scalar decomposition
|
||||||
|
let mut windows: ArrayVec<CellValue<pallas::Base>, NUM_WINDOWS> = ArrayVec::new();
|
||||||
|
|
||||||
|
let scalar_windows: Vec<Option<pallas::Base>> = if let Some(windows) = scalar_windows {
|
||||||
|
assert_eq!(windows.len(), NUM_WINDOWS);
|
||||||
|
windows
|
||||||
|
.into_iter()
|
||||||
|
.map(|window| Some(pallas::Base::from_u64(window as u64)))
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
vec![None; NUM_WINDOWS]
|
||||||
|
};
|
||||||
|
|
||||||
|
for (idx, window) in scalar_windows.into_iter().enumerate() {
|
||||||
|
let window_cell = region.assign_advice(
|
||||||
|
|| format!("k[{:?}]", offset + idx),
|
||||||
|
self.window,
|
||||||
|
offset + idx,
|
||||||
|
|| window.ok_or(Error::SynthesisError),
|
||||||
|
)?;
|
||||||
|
windows.push(CellValue::new(window_cell, window));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(windows)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
use super::super::{EccConfig, EccScalarFixed};
|
||||||
|
use crate::constants::{L_ORCHARD_SCALAR, NUM_WINDOWS};
|
||||||
|
use halo2::{circuit::Region, plonk::Error};
|
||||||
|
use pasta_curves::pallas;
|
||||||
|
|
||||||
|
pub struct Config(super::Config);
|
||||||
|
|
||||||
|
impl From<&EccConfig> for Config {
|
||||||
|
fn from(ecc_config: &EccConfig) -> Self {
|
||||||
|
Self(ecc_config.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn assign_region(
|
||||||
|
&self,
|
||||||
|
value: Option<pallas::Scalar>,
|
||||||
|
offset: usize,
|
||||||
|
region: &mut Region<'_, pallas::Base>,
|
||||||
|
) -> Result<EccScalarFixed, Error> {
|
||||||
|
let windows = self
|
||||||
|
.0
|
||||||
|
.decompose_scalar_fixed::<NUM_WINDOWS, L_ORCHARD_SCALAR>(value, offset, region)?;
|
||||||
|
|
||||||
|
Ok(EccScalarFixed { value, windows })
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
use super::super::{CellValue, EccConfig, EccScalarFixedShort, Var};
|
||||||
|
use crate::constants::{L_VALUE, NUM_WINDOWS_SHORT};
|
||||||
|
use ff::PrimeFieldBits;
|
||||||
|
use halo2::{
|
||||||
|
circuit::Region,
|
||||||
|
plonk::{ConstraintSystem, Error, Expression, Selector},
|
||||||
|
poly::Rotation,
|
||||||
|
};
|
||||||
|
use pasta_curves::{arithmetic::FieldExt, pallas};
|
||||||
|
|
||||||
|
pub struct Config {
|
||||||
|
q_scalar_fixed_short: Selector,
|
||||||
|
super_config: super::Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&EccConfig> for Config {
|
||||||
|
fn from(ecc_config: &EccConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
q_scalar_fixed_short: ecc_config.q_scalar_fixed_short,
|
||||||
|
super_config: ecc_config.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) {
|
||||||
|
// Check that sign is either 1 or -1.
|
||||||
|
meta.create_gate("check sign", |meta| {
|
||||||
|
let q_scalar_fixed_short = meta.query_selector(self.q_scalar_fixed_short);
|
||||||
|
let sign = meta.query_advice(self.super_config.window, Rotation::cur());
|
||||||
|
|
||||||
|
vec![
|
||||||
|
q_scalar_fixed_short
|
||||||
|
* (sign.clone() + Expression::Constant(pallas::Base::one()))
|
||||||
|
* (sign - Expression::Constant(pallas::Base::one())),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
#[allow(clippy::op_ref)]
|
||||||
|
pub fn assign_region(
|
||||||
|
&self,
|
||||||
|
value: Option<pallas::Scalar>,
|
||||||
|
offset: usize,
|
||||||
|
region: &mut Region<'_, pallas::Base>,
|
||||||
|
) -> Result<EccScalarFixedShort, Error>
|
||||||
|
where
|
||||||
|
pallas::Scalar: PrimeFieldBits,
|
||||||
|
{
|
||||||
|
// Enable `q_scalar_fixed_short`
|
||||||
|
self.q_scalar_fixed_short
|
||||||
|
.enable(region, offset + NUM_WINDOWS_SHORT)?;
|
||||||
|
|
||||||
|
// Compute the scalar's sign and magnitude
|
||||||
|
let sign = value.map(|value| {
|
||||||
|
// t = (p - 1)/2
|
||||||
|
let t = (pallas::Scalar::zero() - &pallas::Scalar::one()) * &pallas::Scalar::TWO_INV;
|
||||||
|
if value > t {
|
||||||
|
-pallas::Scalar::one()
|
||||||
|
} else {
|
||||||
|
pallas::Scalar::one()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let magnitude = sign.zip(value).map(|(sign, value)| sign * &value);
|
||||||
|
|
||||||
|
// Decompose magnitude into `k`-bit windows
|
||||||
|
let windows = self
|
||||||
|
.super_config
|
||||||
|
.decompose_scalar_fixed::<NUM_WINDOWS_SHORT, L_VALUE>(magnitude, offset, region)?;
|
||||||
|
|
||||||
|
// Assign the sign and enable `q_scalar_fixed_short`
|
||||||
|
let sign = sign.map(|sign| {
|
||||||
|
assert!(sign == pallas::Scalar::one() || sign == -pallas::Scalar::one());
|
||||||
|
if sign == pallas::Scalar::one() {
|
||||||
|
pallas::Base::one()
|
||||||
|
} else {
|
||||||
|
-pallas::Base::one()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let sign_cell = region.assign_advice(
|
||||||
|
|| "sign",
|
||||||
|
self.super_config.window,
|
||||||
|
NUM_WINDOWS_SHORT,
|
||||||
|
|| sign.ok_or(Error::SynthesisError),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(EccScalarFixedShort {
|
||||||
|
magnitude,
|
||||||
|
sign: CellValue::<pallas::Base>::new(sign_cell, sign),
|
||||||
|
windows,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue