gadget::ecc.rs: Inline witness_scalar_* APIs.

Witness a scalar in the region where it is used for multiplication,
instead of witnessing it separately and then copying it in.
This commit is contained in:
therealyingtong 2021-07-10 13:19:42 +08:00
parent 32f28ed4b0
commit 426f954b1d
11 changed files with 329 additions and 589 deletions

View File

@ -1,10 +1,9 @@
//! Gadgets for elliptic curve operations. //! Gadgets for elliptic curve operations.
use ff::Field;
use std::fmt::Debug; use std::fmt::Debug;
use halo2::{ use halo2::{
arithmetic::{CurveAffine, FieldExt}, arithmetic::CurveAffine,
circuit::{Chip, Layouter}, circuit::{Chip, Layouter},
plonk::Error, plonk::Error,
}; };
@ -53,30 +52,6 @@ pub trait EccInstructions<C: CurveAffine>: Chip<C::Base> + UtilitiesInstructions
b: &Self::Point, b: &Self::Point,
) -> Result<(), Error>; ) -> Result<(), Error>;
/// Witnesses the given base field element as a private input to the circuit
/// for variable-base scalar mul.
fn witness_scalar_var(
&self,
layouter: &mut impl Layouter<C::Base>,
value: Option<C::Base>,
) -> Result<Self::ScalarVar, Error>;
/// Witnesses the given full-width scalar as a private input to the circuit
/// for fixed-base scalar mul.
fn witness_scalar_fixed(
&self,
layouter: &mut impl Layouter<C::Base>,
value: Option<C::Scalar>,
) -> Result<Self::ScalarFixed, Error>;
/// Witnesses the given signed short scalar as a private input to the circuit
/// for fixed-base scalar mul.
fn witness_scalar_fixed_short(
&self,
layouter: &mut impl Layouter<C::Base>,
value: Option<C::Scalar>,
) -> Result<Self::ScalarFixedShort, Error>;
/// Witnesses the given point as a private input to the circuit. /// Witnesses the given point as a private input to the circuit.
/// This maps the identity to (0, 0) in affine coordinates. /// This maps the identity to (0, 0) in affine coordinates.
fn witness_point( fn witness_point(
@ -111,25 +86,25 @@ pub trait EccInstructions<C: CurveAffine>: Chip<C::Base> + UtilitiesInstructions
fn mul( fn mul(
&self, &self,
layouter: &mut impl Layouter<C::Base>, layouter: &mut impl Layouter<C::Base>,
scalar: &Self::ScalarVar, scalar: &Self::Var,
base: &Self::Point, base: &Self::Point,
) -> Result<Self::Point, Error>; ) -> Result<(Self::Point, Self::ScalarVar), Error>;
/// Performs fixed-base scalar multiplication using a full-width scalar, returning `[scalar] base`. /// Performs fixed-base scalar multiplication using a full-width scalar, returning `[scalar] base`.
fn mul_fixed( fn mul_fixed(
&self, &self,
layouter: &mut impl Layouter<C::Base>, layouter: &mut impl Layouter<C::Base>,
scalar: &Self::ScalarFixed, scalar: Option<C::Scalar>,
base: &Self::FixedPoints, base: &Self::FixedPoints,
) -> Result<Self::Point, Error>; ) -> Result<(Self::Point, Self::ScalarFixed), Error>;
/// Performs fixed-base scalar multiplication using a short signed scalar, returning `[scalar] base`. /// Performs fixed-base scalar multiplication using a short signed scalar, returning `[scalar] base`.
fn mul_fixed_short( fn mul_fixed_short(
&self, &self,
layouter: &mut impl Layouter<C::Base>, layouter: &mut impl Layouter<C::Base>,
scalar: &Self::ScalarFixedShort, scalar: Option<C::Scalar>,
base: &Self::FixedPointsShort, base: &Self::FixedPointsShort,
) -> Result<Self::Point, Error>; ) -> Result<(Self::Point, Self::ScalarFixedShort), Error>;
/// Performs fixed-base scalar multiplication using a base field element as the scalar. /// Performs fixed-base scalar multiplication using a base field element as the scalar.
/// In the current implementation, this base field element must be output from another /// In the current implementation, this base field element must be output from another
@ -159,18 +134,6 @@ pub struct ScalarVar<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug
inner: EccChip::ScalarVar, inner: EccChip::ScalarVar,
} }
impl<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> ScalarVar<C, EccChip> {
/// Constructs a new ScalarVar with the given value.
pub fn new(
chip: EccChip,
mut layouter: impl Layouter<C::Base>,
value: Option<C::Base>,
) -> Result<Self, Error> {
chip.witness_scalar_var(&mut layouter, value)
.map(|inner| ScalarVar { chip, inner })
}
}
/// A full-width element of the given elliptic curve's scalar field, to be used for fixed-base scalar mul. /// A full-width element of the given elliptic curve's scalar field, to be used for fixed-base scalar mul.
#[derive(Debug)] #[derive(Debug)]
pub struct ScalarFixed<C: CurveAffine, EccChip> pub struct ScalarFixed<C: CurveAffine, EccChip>
@ -181,21 +144,6 @@ where
inner: EccChip::ScalarFixed, inner: EccChip::ScalarFixed,
} }
impl<C: CurveAffine, EccChip> ScalarFixed<C, EccChip>
where
EccChip: EccInstructions<C> + Clone + Debug + Eq,
{
/// Constructs a new ScalarFixed with the given value.
pub fn new(
chip: EccChip,
mut layouter: impl Layouter<C::Base>,
value: Option<C::Scalar>,
) -> Result<Self, Error> {
chip.witness_scalar_fixed(&mut layouter, value)
.map(|inner| ScalarFixed { chip, inner })
}
}
/// A signed short element of the given elliptic curve's scalar field, to be used for fixed-base scalar mul. /// A signed short element of the given elliptic curve's scalar field, to be used for fixed-base scalar mul.
#[derive(Debug)] #[derive(Debug)]
pub struct ScalarFixedShort<C: CurveAffine, EccChip> pub struct ScalarFixedShort<C: CurveAffine, EccChip>
@ -206,39 +154,6 @@ where
inner: EccChip::ScalarFixedShort, inner: EccChip::ScalarFixedShort,
} }
impl<C: CurveAffine, EccChip> ScalarFixedShort<C, EccChip>
where
EccChip: EccInstructions<C> + Clone + Debug + Eq,
{
/// Constructs a new ScalarFixedShort with the given value.
///
/// # Panics
///
/// The short scalar must be in the range [-(2^64 - 1), (2^64 - 1)].
pub fn new(
chip: EccChip,
mut layouter: impl Layouter<C::Base>,
value: Option<C::Scalar>,
) -> Result<Self, Error> {
// Check that the scalar is in the range [-(2^64 - 1), (2^64 - 1)]
if let Some(value) = value {
let mut sign = C::Scalar::one();
// T = (p-1) / 2
let t = (C::Scalar::zero() - C::Scalar::one()) * C::Scalar::TWO_INV;
if value > t {
sign = -sign;
}
let magnitude = value * sign;
assert!(magnitude < C::Scalar::from_u128(1 << 64));
}
chip.witness_scalar_fixed_short(&mut layouter, value)
.map(|inner| ScalarFixedShort { chip, inner })
}
}
/// An elliptic curve point over the given curve. /// An elliptic curve point over the given curve.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct Point<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> { pub struct Point<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> {
@ -307,14 +222,21 @@ impl<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> Point<C,
pub fn mul( pub fn mul(
&self, &self,
mut layouter: impl Layouter<C::Base>, mut layouter: impl Layouter<C::Base>,
by: &ScalarVar<C, EccChip>, by: &EccChip::Var,
) -> Result<Self, Error> { ) -> Result<(Self, ScalarVar<C, EccChip>), Error> {
assert_eq!(self.chip, by.chip);
self.chip self.chip
.mul(&mut layouter, &by.inner, &self.inner) .mul(&mut layouter, by, &self.inner)
.map(|inner| Point { .map(|(point, scalar)| {
(
Point {
chip: self.chip.clone(), chip: self.chip.clone(),
inner, inner: point,
},
ScalarVar {
chip: self.chip.clone(),
inner: scalar,
},
)
}) })
} }
} }
@ -353,14 +275,21 @@ where
pub fn mul( pub fn mul(
&self, &self,
mut layouter: impl Layouter<C::Base>, mut layouter: impl Layouter<C::Base>,
by: &ScalarFixed<C, EccChip>, by: Option<C::Scalar>,
) -> Result<Point<C, EccChip>, Error> { ) -> Result<(Point<C, EccChip>, ScalarFixed<C, EccChip>), Error> {
assert_eq!(self.chip, by.chip);
self.chip self.chip
.mul_fixed(&mut layouter, &by.inner, &self.inner) .mul_fixed(&mut layouter, by, &self.inner)
.map(|inner| Point { .map(|(point, scalar)| {
(
Point {
chip: self.chip.clone(), chip: self.chip.clone(),
inner, inner: point,
},
ScalarFixed {
chip: self.chip.clone(),
inner: scalar,
},
)
}) })
} }
@ -404,14 +333,21 @@ where
pub fn mul( pub fn mul(
&self, &self,
mut layouter: impl Layouter<C::Base>, mut layouter: impl Layouter<C::Base>,
by: &ScalarFixedShort<C, EccChip>, by: Option<C::Scalar>,
) -> Result<Point<C, EccChip>, Error> { ) -> Result<(Point<C, EccChip>, ScalarFixedShort<C, EccChip>), Error> {
assert_eq!(self.chip, by.chip);
self.chip self.chip
.mul_fixed_short(&mut layouter, &by.inner, &self.inner) .mul_fixed_short(&mut layouter, by, &self.inner)
.map(|inner| Point { .map(|(point, scalar)| {
(
Point {
chip: self.chip.clone(), chip: self.chip.clone(),
inner, inner: point,
},
ScalarFixedShort {
chip: self.chip.clone(),
inner: scalar,
},
)
}) })
} }

View File

@ -21,7 +21,6 @@ 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;
/// 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.
@ -248,30 +247,15 @@ impl EccChip {
mul_config.create_gate(meta); mul_config.create_gate(meta);
} }
// Create witness scalar_fixed gate that applies to both full-width and // Create gate that is only used in full-width fixed-base scalar mul.
// short scalars
{ {
let config: witness_scalar_fixed::Config = (&config).into(); let mul_fixed_full_config: mul_fixed::full_width::Config = (&config).into();
config.create_gate(meta); mul_fixed_full_config.create_gate(meta);
}
// Create witness scalar_fixed gate that only applies to short scalars
{
let config: witness_scalar_fixed::short::Config = (&config).into();
config.create_gate(meta);
}
// Create fixed-base scalar mul gate that is used in both full-width
// and short multiplication.
{
let mul_fixed_config: mul_fixed::Config<{ constants::NUM_WINDOWS }> = (&config).into();
mul_fixed_config.create_gate_scalar(meta);
} }
// Create gate that is only used in short fixed-base scalar mul. // Create gate that is only used in short fixed-base scalar mul.
{ {
let short_config: mul_fixed::short::Config<{ constants::NUM_WINDOWS_SHORT }> = let short_config: mul_fixed::short::Config = (&config).into();
(&config).into();
short_config.create_gate(meta); short_config.create_gate(meta);
} }
@ -285,17 +269,6 @@ impl EccChip {
} }
} }
/// A base-field element used as the scalar in variable-base scalar multiplication.
#[derive(Copy, Clone, Debug)]
pub struct EccScalarVar(CellValue<pallas::Base>);
impl std::ops::Deref for EccScalarVar {
type Target = CellValue<pallas::Base>;
fn deref(&self) -> &CellValue<pallas::Base> {
&self.0
}
}
/// A full-width scalar used for fixed-base scalar multiplication. /// A full-width scalar used for fixed-base scalar multiplication.
/// This is decomposed into 85 3-bit windows in little-endian order, /// This is decomposed into 85 3-bit windows in little-endian order,
/// i.e. `windows` = [k_0, k_1, ..., k_84] (for a 255-bit scalar) /// i.e. `windows` = [k_0, k_1, ..., k_84] (for a 255-bit scalar)
@ -350,7 +323,7 @@ impl EccBaseFieldElemFixed {
impl EccInstructions<pallas::Affine> for EccChip { impl EccInstructions<pallas::Affine> for EccChip {
type ScalarFixed = EccScalarFixed; type ScalarFixed = EccScalarFixed;
type ScalarFixedShort = EccScalarFixedShort; type ScalarFixedShort = EccScalarFixedShort;
type ScalarVar = EccScalarVar; type ScalarVar = CellValue<pallas::Base>;
type Point = EccPoint; type Point = EccPoint;
type X = CellValue<pallas::Base>; type X = CellValue<pallas::Base>;
type FixedPoints = OrchardFixedBasesFull; type FixedPoints = OrchardFixedBasesFull;
@ -374,50 +347,6 @@ impl EccInstructions<pallas::Affine> for EccChip {
) )
} }
fn witness_scalar_var(
&self,
layouter: &mut impl Layouter<pallas::Base>,
value: Option<pallas::Base>,
) -> Result<Self::ScalarVar, Error> {
let config = self.config().clone();
layouter.assign_region(
|| "Witness scalar for variable-base mul",
|mut region| {
let cell = region.assign_advice(
|| "witness scalar var",
config.advices[0],
0,
|| value.ok_or(Error::SynthesisError),
)?;
Ok(EccScalarVar(CellValue::new(cell, value)))
},
)
}
fn witness_scalar_fixed(
&self,
layouter: &mut impl Layouter<pallas::Base>,
value: Option<pallas::Scalar>,
) -> Result<Self::ScalarFixed, Error> {
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(
&self,
layouter: &mut impl Layouter<pallas::Base>,
value: Option<pallas::Scalar>,
) -> Result<Self::ScalarFixedShort, Error> {
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(
&self, &self,
layouter: &mut impl Layouter<pallas::Base>, layouter: &mut impl Layouter<pallas::Base>,
@ -463,9 +392,9 @@ impl EccInstructions<pallas::Affine> for EccChip {
fn mul( fn mul(
&self, &self,
layouter: &mut impl Layouter<pallas::Base>, layouter: &mut impl Layouter<pallas::Base>,
scalar: &Self::ScalarVar, scalar: &Self::Var,
base: &Self::Point, base: &Self::Point,
) -> Result<Self::Point, Error> { ) -> Result<(Self::Point, Self::ScalarVar), Error> {
let config: mul::Config = self.config().into(); let config: mul::Config = self.config().into();
config.assign( config.assign(
layouter.namespace(|| "variable-base scalar mul"), layouter.namespace(|| "variable-base scalar mul"),
@ -477,11 +406,10 @@ impl EccInstructions<pallas::Affine> for EccChip {
fn mul_fixed( fn mul_fixed(
&self, &self,
layouter: &mut impl Layouter<pallas::Base>, layouter: &mut impl Layouter<pallas::Base>,
scalar: &Self::ScalarFixed, scalar: Option<pallas::Scalar>,
base: &Self::FixedPoints, base: &Self::FixedPoints,
) -> Result<Self::Point, Error> { ) -> Result<(Self::Point, Self::ScalarFixed), Error> {
let config: mul_fixed::full_width::Config<{ constants::NUM_WINDOWS }> = let config: mul_fixed::full_width::Config = self.config().into();
self.config().into();
config.assign( config.assign(
layouter.namespace(|| format!("fixed-base mul of {:?}", base)), layouter.namespace(|| format!("fixed-base mul of {:?}", base)),
scalar, scalar,
@ -492,11 +420,10 @@ impl EccInstructions<pallas::Affine> for EccChip {
fn mul_fixed_short( fn mul_fixed_short(
&self, &self,
layouter: &mut impl Layouter<pallas::Base>, layouter: &mut impl Layouter<pallas::Base>,
scalar: &Self::ScalarFixedShort, scalar: Option<pallas::Scalar>,
base: &Self::FixedPointsShort, base: &Self::FixedPointsShort,
) -> Result<Self::Point, Error> { ) -> Result<(Self::Point, Self::ScalarFixedShort), Error> {
let config: mul_fixed::short::Config<{ constants::NUM_WINDOWS_SHORT }> = let config: mul_fixed::short::Config = self.config().into();
self.config().into();
config.assign( config.assign(
layouter.namespace(|| format!("short fixed-base mul of {:?}", base)), layouter.namespace(|| format!("short fixed-base mul of {:?}", base)),
scalar, scalar,

View File

@ -1,4 +1,4 @@
use super::{add, CellValue, EccConfig, EccPoint, EccScalarVar, Var}; use super::{add, CellValue, EccConfig, EccPoint, Var};
use crate::{circuit::gadget::utilities::copy, constants::T_Q}; use crate::{circuit::gadget::utilities::copy, constants::T_Q};
use std::ops::{Deref, Range}; use std::ops::{Deref, Range};
@ -137,9 +137,9 @@ impl Config {
pub(super) fn assign( pub(super) fn assign(
&self, &self,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
alpha: EccScalarVar, alpha: CellValue<pallas::Base>,
base: &EccPoint, base: &EccPoint,
) -> Result<EccPoint, Error> { ) -> Result<(EccPoint, CellValue<pallas::Base>), Error> {
let (result, zs): (EccPoint, Vec<Z<pallas::Base>>) = layouter.assign_region( let (result, zs): (EccPoint, Vec<Z<pallas::Base>>) = layouter.assign_region(
|| "variable-base scalar mul", || "variable-base scalar mul",
|mut region| { |mut region| {
@ -264,7 +264,7 @@ impl Config {
self.overflow_config self.overflow_config
.overflow_check(layouter.namespace(|| "overflow check"), alpha, &zs)?; .overflow_check(layouter.namespace(|| "overflow check"), alpha, &zs)?;
Ok(result) Ok((result, alpha))
} }
/// Processes the final scalar bit `k_0`. /// Processes the final scalar bit `k_0`.
@ -448,18 +448,26 @@ fn decompose_for_scalar_mul(scalar: Option<pallas::Base>) -> Vec<Option<bool>> {
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use group::Curve; use group::Curve;
use halo2::{circuit::Layouter, plonk::Error}; use halo2::{
circuit::{Chip, Layouter},
plonk::Error,
};
use pasta_curves::{arithmetic::FieldExt, pallas}; use pasta_curves::{arithmetic::FieldExt, pallas};
use crate::circuit::gadget::ecc::{EccInstructions, Point, ScalarVar}; use crate::circuit::gadget::{
ecc::{chip::EccChip, EccInstructions, Point},
utilities::UtilitiesInstructions,
};
pub fn test_mul<EccChip: EccInstructions<pallas::Affine> + Clone + Eq + std::fmt::Debug>( pub fn test_mul(
chip: EccChip, chip: EccChip,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
zero: &Point<pallas::Affine, EccChip>, zero: &Point<pallas::Affine, EccChip>,
p: &Point<pallas::Affine, EccChip>, p: &Point<pallas::Affine, EccChip>,
p_val: pallas::Affine, p_val: pallas::Affine,
) -> Result<(), Error> { ) -> Result<(), Error> {
let column = chip.config().advices[0];
fn constrain_equal< fn constrain_equal<
EccChip: EccInstructions<pallas::Affine> + Clone + Eq + std::fmt::Debug, EccChip: EccInstructions<pallas::Affine> + Clone + Eq + std::fmt::Debug,
>( >(
@ -483,10 +491,10 @@ pub mod tests {
// [a]B // [a]B
{ {
let scalar_val = pallas::Base::rand(); let scalar_val = pallas::Base::rand();
let result = { let (result, _) = {
let scalar = ScalarVar::new( let scalar = chip.load_private(
chip.clone(),
layouter.namespace(|| "random scalar"), layouter.namespace(|| "random scalar"),
column,
Some(scalar_val), Some(scalar_val),
)?; )?;
p.mul(layouter.namespace(|| "random [a]B"), &scalar)? p.mul(layouter.namespace(|| "random [a]B"), &scalar)?
@ -504,9 +512,9 @@ pub mod tests {
// uses incomplete addition at the beginning of its double-and-add. // uses incomplete addition at the beginning of its double-and-add.
{ {
let scalar_val = pallas::Base::rand(); let scalar_val = pallas::Base::rand();
let scalar = ScalarVar::new( let scalar = chip.load_private(
chip.clone(),
layouter.namespace(|| "random scalar"), layouter.namespace(|| "random scalar"),
column,
Some(scalar_val), Some(scalar_val),
)?; )?;
zero.mul(layouter.namespace(|| "[a]𝒪"), &scalar) zero.mul(layouter.namespace(|| "[a]𝒪"), &scalar)
@ -517,12 +525,9 @@ pub mod tests {
// uses complete addition for the final bits of the scalar. // uses complete addition for the final bits of the scalar.
{ {
let scalar_val = pallas::Base::zero(); let scalar_val = pallas::Base::zero();
let result = { let (result, _) = {
let scalar = ScalarVar::new( let scalar =
chip.clone(), chip.load_private(layouter.namespace(|| "zero"), column, Some(scalar_val))?;
layouter.namespace(|| "zero"),
Some(scalar_val),
)?;
p.mul(layouter.namespace(|| "[0]B"), &scalar)? p.mul(layouter.namespace(|| "[0]B"), &scalar)?
}; };
constrain_equal( constrain_equal(
@ -537,9 +542,9 @@ pub mod tests {
// [-1]B (the largest possible base field element) // [-1]B (the largest possible base field element)
{ {
let scalar_val = -pallas::Base::one(); let scalar_val = -pallas::Base::one();
let result = { let (result, _) = {
let scalar = let scalar =
ScalarVar::new(chip.clone(), layouter.namespace(|| "-1"), Some(scalar_val))?; chip.load_private(layouter.namespace(|| "-1"), column, Some(scalar_val))?;
p.mul(layouter.namespace(|| "[-1]B"), &scalar)? p.mul(layouter.namespace(|| "[-1]B"), &scalar)?
}; };
constrain_equal( constrain_equal(

View File

@ -1,4 +1,4 @@
use super::super::{copy, CellValue, EccConfig, EccScalarVar, Var}; use super::super::{copy, CellValue, EccConfig, Var};
use super::Z; use super::Z;
use crate::{ use crate::{
circuit::gadget::utilities::lookup_range_check::LookupRangeCheckConfig, constants::T_Q, circuit::gadget::utilities::lookup_range_check::LookupRangeCheckConfig, constants::T_Q,
@ -95,7 +95,7 @@ impl Config {
pub(super) fn overflow_check( pub(super) fn overflow_check(
&self, &self,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
alpha: EccScalarVar, alpha: CellValue<pallas::Base>,
zs: &[Z<pallas::Base>], // [z_0, z_1, ..., z_{254}, z_{255}] zs: &[Z<pallas::Base>], // [z_0, z_1, ..., z_{254}, z_{255}]
) -> Result<(), Error> { ) -> Result<(), Error> {
// s = alpha + k_254 ⋅ 2^130 is witnessed here, and then copied into // s = alpha + k_254 ⋅ 2^130 is witnessed here, and then copied into
@ -189,7 +189,7 @@ impl Config {
|| "copy original alpha", || "copy original alpha",
self.advices[1], self.advices[1],
offset + 1, offset + 1,
&*alpha, &alpha,
&self.perm, &self.perm,
)?; )?;

View File

@ -1,19 +1,18 @@
use super::{ use super::{
add, add_incomplete, copy, CellValue, EccBaseFieldElemFixed, EccConfig, EccPoint, add, add_incomplete, CellValue, EccBaseFieldElemFixed, EccConfig, EccPoint, EccScalarFixed,
EccScalarFixed, EccScalarFixedShort, Var, EccScalarFixedShort, Var,
}; };
use crate::constants::{ use crate::constants::{
self, self,
load::{OrchardFixedBase, OrchardFixedBasesFull, ValueCommitV, WindowUs}, load::{OrchardFixedBase, OrchardFixedBasesFull, ValueCommitV, WindowUs},
util,
}; };
use arrayvec::ArrayVec;
use group::Curve; use group::Curve;
use halo2::{ use halo2::{
circuit::Region, circuit::Region,
plonk::{ plonk::{Advice, Column, Error, Expression, Fixed, Permutation, Selector, VirtualCells},
Advice, Column, ConstraintSystem, Error, Expression, Fixed, Permutation, Selector,
VirtualCells,
},
poly::Rotation, poly::Rotation,
}; };
use lazy_static::lazy_static; use lazy_static::lazy_static;
@ -72,6 +71,7 @@ impl OrchardFixedBases {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Config<const NUM_WINDOWS: usize> { pub struct Config<const NUM_WINDOWS: usize> {
q_mul_fixed: Selector, q_mul_fixed: Selector,
q_scalar_fixed: Selector,
// The fixed Lagrange interpolation coefficients for `x_p`. // The fixed Lagrange interpolation coefficients for `x_p`.
lagrange_coeffs: [Column<Fixed>; constants::H], lagrange_coeffs: [Column<Fixed>; constants::H],
// The fixed `z` for each window such that `y + z = u^2`. // The fixed `z` for each window such that `y + z = u^2`.
@ -97,6 +97,7 @@ impl<const NUM_WINDOWS: usize> From<&EccConfig> for Config<NUM_WINDOWS> {
fn from(ecc_config: &EccConfig) -> Self { fn from(ecc_config: &EccConfig) -> Self {
let config = Self { let config = Self {
q_mul_fixed: ecc_config.q_mul_fixed, q_mul_fixed: ecc_config.q_mul_fixed,
q_scalar_fixed: ecc_config.q_scalar_fixed,
lagrange_coeffs: ecc_config.lagrange_coeffs, lagrange_coeffs: ecc_config.lagrange_coeffs,
fixed_z: ecc_config.fixed_z, fixed_z: ecc_config.fixed_z,
x_p: ecc_config.advices[0], x_p: ecc_config.advices[0],
@ -143,17 +144,6 @@ impl<const NUM_WINDOWS: usize> From<&EccConfig> for Config<NUM_WINDOWS> {
} }
impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> { impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
pub(super) fn create_gate_scalar(&self, meta: &mut ConstraintSystem<pallas::Base>) {
meta.create_gate(
"x_p, y_p checks for ScalarFixed, ScalarFixedShort",
|meta| {
let mul_fixed = meta.query_selector(self.q_mul_fixed);
let window = meta.query_advice(self.window, Rotation::cur());
self.coords_check(meta, mul_fixed, window)
},
)
}
#[allow(clippy::op_ref)] #[allow(clippy::op_ref)]
fn coords_check( fn coords_check(
&self, &self,
@ -288,25 +278,53 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
Ok(()) Ok(())
} }
fn copy_scalar( /// Witnesses the given scalar as `NUM_WINDOWS` 3-bit windows.
///
/// The scalar is allowed to be non-canonical.
fn decompose_scalar_fixed<const SCALAR_NUM_BITS: usize>(
&self, &self,
region: &mut Region<'_, pallas::Base>, scalar: Option<pallas::Scalar>,
offset: usize, offset: usize,
scalar: &ScalarFixed, region: &mut Region<'_, pallas::Base>,
) -> Result<(), Error> { ) -> Result<ArrayVec<CellValue<pallas::Base>, NUM_WINDOWS>, Error> {
// Copy the scalar decomposition (`k`-bit windows) // Enable `q_scalar_fixed` selector
for (window_idx, window) in scalar.windows().iter().enumerate() { for idx in 0..NUM_WINDOWS {
copy( self.q_scalar_fixed.enable(region, offset + idx)?;
region,
|| format!("k[{:?}]", window),
self.window,
window_idx + offset,
window,
&self.perm,
)?;
} }
Ok(()) // Decompose scalar into `k-bit` windows
let scalar_windows: Option<Vec<u8>> = scalar.map(|scalar| {
util::decompose_word::<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)
} }
fn process_window( fn process_window(

View File

@ -490,6 +490,7 @@ pub mod tests {
base_val: pallas::Affine, base_val: pallas::Affine,
) -> Result<(), Error> { ) -> Result<(), Error> {
let column = chip.config().advices[0]; let column = chip.config().advices[0];
fn constrain_equal( fn constrain_equal(
chip: EccChip, chip: EccChip,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,

View File

@ -1,38 +1,90 @@
use super::super::{EccConfig, EccPoint, EccScalarFixed, OrchardFixedBasesFull}; use super::super::{EccConfig, EccPoint, EccScalarFixed, OrchardFixedBasesFull};
use halo2::{circuit::Layouter, plonk::Error}; use crate::{
circuit::gadget::utilities::range_check,
constants::{self, L_ORCHARD_SCALAR, NUM_WINDOWS},
};
use halo2::{
circuit::{Layouter, Region},
plonk::{ConstraintSystem, Error, Selector},
poly::Rotation,
};
use pasta_curves::pallas; use pasta_curves::pallas;
pub struct Config<const NUM_WINDOWS: usize>(super::Config<NUM_WINDOWS>); pub struct Config {
q_scalar_fixed: Selector,
super_config: super::Config<NUM_WINDOWS>,
}
impl<const NUM_WINDOWS: usize> From<&EccConfig> for Config<NUM_WINDOWS> { impl From<&EccConfig> for Config {
fn from(config: &EccConfig) -> Self { fn from(config: &EccConfig) -> Self {
Self(config.into()) Self {
q_scalar_fixed: config.q_scalar_fixed,
super_config: config.into(),
}
} }
} }
impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> { impl Config {
pub fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) {
// Check that each window `k` is within 3 bits
meta.create_gate("Full-width fixed-base scalar mul", |meta| {
let q_scalar_fixed = meta.query_selector(self.q_scalar_fixed);
let window = meta.query_advice(self.super_config.window, Rotation::cur());
self.super_config
.coords_check(meta, q_scalar_fixed.clone(), window.clone())
.into_iter()
// Constrain each window to a 3-bit value:
// 1 * (window - 0) * (window - 1) * ... * (window - 7)
.chain(Some((
"window range check",
q_scalar_fixed * range_check(window, constants::H),
)))
});
}
/// Witnesses the given scalar as `NUM_WINDOWS` 3-bit windows.
///
/// The scalar is allowed to be non-canonical.
fn witness(
&self,
region: &mut Region<'_, pallas::Base>,
offset: usize,
scalar: Option<pallas::Scalar>,
) -> Result<EccScalarFixed, Error> {
let windows = self
.super_config
.decompose_scalar_fixed::<L_ORCHARD_SCALAR>(scalar, offset, region)?;
Ok(EccScalarFixed {
value: scalar,
windows,
})
}
pub fn assign( pub fn assign(
&self, &self,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
scalar: &EccScalarFixed, scalar: Option<pallas::Scalar>,
base: OrchardFixedBasesFull, base: OrchardFixedBasesFull,
) -> Result<EccPoint, Error> { ) -> Result<(EccPoint, EccScalarFixed), Error> {
let (acc, mul_b) = layouter.assign_region( let (scalar, acc, mul_b) = layouter.assign_region(
|| "Full-width fixed-base mul (incomplete addition)", || "Full-width fixed-base mul (incomplete addition)",
|mut region| { |mut region| {
let offset = 0; let offset = 0;
// Copy the scalar decomposition let scalar = self.witness(&mut region, offset, scalar)?;
self.0.copy_scalar(&mut region, offset, &scalar.into())?;
self.0.assign_region_inner( let (acc, mul_b) = self.super_config.assign_region_inner(
&mut region, &mut region,
offset, offset,
&scalar.into(), &(&scalar).into(),
base.into(), base.into(),
self.0.q_mul_fixed, self.super_config.q_mul_fixed,
) )?;
Ok((scalar, acc, mul_b))
}, },
)?; )?;
@ -40,7 +92,7 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
let result = layouter.assign_region( let result = layouter.assign_region(
|| "Full-width fixed-base mul (last window, complete addition)", || "Full-width fixed-base mul (last window, complete addition)",
|mut region| { |mut region| {
self.0 self.super_config
.add_config .add_config
.assign_region(&mul_b, &acc, 0, &mut region) .assign_region(&mul_b, &acc, 0, &mut region)
}, },
@ -60,7 +112,7 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
} }
} }
Ok(result) Ok((result, scalar))
} }
} }
@ -72,7 +124,7 @@ pub mod tests {
use crate::circuit::gadget::ecc::{ use crate::circuit::gadget::ecc::{
chip::{EccChip, OrchardFixedBasesFull}, chip::{EccChip, OrchardFixedBasesFull},
FixedPoint, Point, ScalarFixed, FixedPoint, Point,
}; };
use crate::constants; use crate::constants;
@ -154,15 +206,7 @@ pub mod tests {
{ {
let scalar_fixed = pallas::Scalar::rand(); let scalar_fixed = pallas::Scalar::rand();
let result = { let (result, _) = base.mul(layouter.namespace(|| "random [a]B"), Some(scalar_fixed))?;
let scalar_fixed = ScalarFixed::new(
chip.clone(),
layouter.namespace(|| "random scalar"),
Some(scalar_fixed),
)?;
base.mul(layouter.namespace(|| "random [a]B"), &scalar_fixed)?
};
constrain_equal( constrain_equal(
chip.clone(), chip.clone(),
layouter.namespace(|| "random [a]B"), layouter.namespace(|| "random [a]B"),
@ -183,14 +227,8 @@ pub mod tests {
.fold(pallas::Scalar::zero(), |acc, c| { .fold(pallas::Scalar::zero(), |acc, c| {
acc * &h + &pallas::Scalar::from_u64(c.to_digit(8).unwrap().into()) acc * &h + &pallas::Scalar::from_u64(c.to_digit(8).unwrap().into())
}); });
let result = { let (result, _) =
let scalar_fixed = ScalarFixed::new( base.mul(layouter.namespace(|| "mul with double"), Some(scalar_fixed))?;
chip.clone(),
layouter.namespace(|| "mul with double"),
Some(scalar_fixed),
)?;
base.mul(layouter.namespace(|| "mul with double"), &scalar_fixed)?
};
constrain_equal( constrain_equal(
chip.clone(), chip.clone(),
@ -205,14 +243,7 @@ pub mod tests {
// on the last step. // on the last step.
{ {
let scalar_fixed = pallas::Scalar::zero(); let scalar_fixed = pallas::Scalar::zero();
let result = { let (result, _) = base.mul(layouter.namespace(|| "mul by zero"), Some(scalar_fixed))?;
let scalar_fixed = ScalarFixed::new(
chip.clone(),
layouter.namespace(|| "zero"),
Some(scalar_fixed),
)?;
base.mul(layouter.namespace(|| "mul by zero"), &scalar_fixed)?
};
constrain_equal( constrain_equal(
chip.clone(), chip.clone(),
layouter.namespace(|| "mul by zero"), layouter.namespace(|| "mul by zero"),
@ -225,14 +256,7 @@ pub mod tests {
// [-1]B is the largest scalar field element. // [-1]B is the largest scalar field element.
{ {
let scalar_fixed = -pallas::Scalar::one(); let scalar_fixed = -pallas::Scalar::one();
let result = { let (result, _) = base.mul(layouter.namespace(|| "mul by -1"), Some(scalar_fixed))?;
let scalar_fixed = ScalarFixed::new(
chip.clone(),
layouter.namespace(|| "-1"),
Some(scalar_fixed),
)?;
base.mul(layouter.namespace(|| "mul by -1"), &scalar_fixed)?
};
constrain_equal( constrain_equal(
chip, chip,
layouter.namespace(|| "mul by -1"), layouter.namespace(|| "mul by -1"),

View File

@ -1,34 +1,54 @@
use std::array; use std::array;
use super::super::{copy, CellValue, EccConfig, EccPoint, EccScalarFixedShort, Var}; use super::super::{copy, CellValue, EccConfig, EccPoint, EccScalarFixedShort, Var};
use crate::constants::ValueCommitV; use crate::constants::{ValueCommitV, L_VALUE, NUM_WINDOWS_SHORT};
use halo2::{ use halo2::{
circuit::Layouter, circuit::{Layouter, Region},
plonk::{ConstraintSystem, Error, Selector}, plonk::{ConstraintSystem, Error, Expression, Selector},
poly::Rotation, poly::Rotation,
}; };
use pasta_curves::pallas; use pasta_curves::{arithmetic::FieldExt, pallas};
pub struct Config<const NUM_WINDOWS: usize> { pub struct Config {
// Selector used for fixed-base scalar mul with short signed exponent. // Selector used for fixed-base scalar mul with short signed exponent.
q_mul_fixed_short: Selector, q_mul_fixed_short: Selector,
super_config: super::Config<NUM_WINDOWS>, q_scalar_fixed_short: Selector,
super_config: super::Config<NUM_WINDOWS_SHORT>,
} }
impl<const NUM_WINDOWS: usize> From<&EccConfig> for Config<NUM_WINDOWS> { impl From<&EccConfig> for Config {
fn from(config: &EccConfig) -> Self { fn from(config: &EccConfig) -> Self {
Self { Self {
q_mul_fixed_short: config.q_mul_fixed_short, q_mul_fixed_short: config.q_mul_fixed_short,
q_scalar_fixed_short: config.q_scalar_fixed_short,
super_config: config.into(), super_config: config.into(),
} }
} }
} }
impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> { impl Config {
// We reuse the constraints in the `mul_fixed` gate so exclude them here. // We reuse the constraints in the `mul_fixed` gate so exclude them here.
// Here, we add some new constraints specific to the short signed case. // Here, we add some new constraints specific to the short signed case.
pub(crate) fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) { pub(crate) fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) {
// Check that sign is either 1 or -1.
// Check that last window is either 0 or 1.
meta.create_gate("Check sign and last window", |meta| {
let q_scalar_fixed_short = meta.query_selector(self.q_scalar_fixed_short);
let last_window = meta.query_advice(self.super_config.window, Rotation::prev());
let sign = meta.query_advice(self.super_config.window, Rotation::cur());
let one = Expression::Constant(pallas::Base::one());
let last_window_check = last_window.clone() * (one.clone() - last_window);
let sign_check = sign.clone() * sign - one;
vec![
q_scalar_fixed_short.clone() * last_window_check,
q_scalar_fixed_short * sign_check,
]
});
meta.create_gate("Short fixed-base mul gate", |meta| { meta.create_gate("Short fixed-base mul gate", |meta| {
let q_mul_fixed_short = meta.query_selector(self.q_mul_fixed_short); let q_mul_fixed_short = meta.query_selector(self.q_mul_fixed_short);
let y_p = meta.query_advice(self.super_config.y_p, Rotation::cur()); let y_p = meta.query_advice(self.super_config.y_p, Rotation::cur());
@ -51,28 +71,80 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
}); });
} }
fn witness(
&self,
region: &mut Region<'_, pallas::Base>,
offset: usize,
value: Option<pallas::Scalar>,
) -> Result<EccScalarFixedShort, Error> {
// 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::<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,
offset + NUM_WINDOWS_SHORT,
|| sign.ok_or(Error::SynthesisError),
)?;
Ok(EccScalarFixedShort {
magnitude,
sign: CellValue::<pallas::Base>::new(sign_cell, sign),
windows,
})
}
pub fn assign( pub fn assign(
&self, &self,
mut layouter: impl Layouter<pallas::Base>, mut layouter: impl Layouter<pallas::Base>,
scalar: &EccScalarFixedShort, scalar: Option<pallas::Scalar>,
base: &ValueCommitV, base: &ValueCommitV,
) -> Result<EccPoint, Error> { ) -> Result<(EccPoint, EccScalarFixedShort), Error> {
let (acc, mul_b) = layouter.assign_region( let (scalar, acc, mul_b) = layouter.assign_region(
|| "Short fixed-base mul (incomplete addition)", || "Short fixed-base mul (incomplete addition)",
|mut region| { |mut region| {
let offset = 0; let offset = 0;
// Copy the scalar decomposition // Copy the scalar decomposition
self.super_config let scalar = self.witness(&mut region, offset, scalar)?;
.copy_scalar(&mut region, offset, &scalar.into())?;
self.super_config.assign_region_inner( let (acc, mul_b) = self.super_config.assign_region_inner(
&mut region, &mut region,
offset, offset,
&scalar.into(), &(&scalar).into(),
base.clone().into(), base.clone().into(),
self.super_config.q_mul_fixed, self.super_config.q_mul_fixed,
) )?;
Ok((scalar, acc, mul_b))
}, },
)?; )?;
@ -158,7 +230,7 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
} }
} }
Ok(result) Ok((result, scalar))
} }
} }
@ -168,7 +240,7 @@ pub mod tests {
use halo2::{circuit::Layouter, plonk::Error}; use halo2::{circuit::Layouter, plonk::Error};
use pasta_curves::{arithmetic::FieldExt, pallas}; use pasta_curves::{arithmetic::FieldExt, pallas};
use crate::circuit::gadget::ecc::{chip::EccChip, FixedPointShort, Point, ScalarFixedShort}; use crate::circuit::gadget::ecc::{chip::EccChip, FixedPointShort, Point};
use crate::constants::load::ValueCommitV; use crate::constants::load::ValueCommitV;
#[allow(clippy::op_ref)] #[allow(clippy::op_ref)]
@ -199,20 +271,17 @@ pub mod tests {
// [0]B should return (0,0) since it uses complete addition // [0]B should return (0,0) since it uses complete addition
// on the last step. // on the last step.
{ {
let scalar_fixed = pallas::Scalar::zero(); let scalar_fixed_short = pallas::Scalar::zero();
let result = { let (result, _) = value_commit_v.mul(
let scalar_fixed = ScalarFixedShort::new( layouter.namespace(|| "mul by zero"),
chip.clone(), Some(scalar_fixed_short),
layouter.namespace(|| "zero"),
Some(scalar_fixed),
)?; )?;
value_commit_v.mul(layouter.namespace(|| "mul by zero"), &scalar_fixed)?
};
constrain_equal( constrain_equal(
chip.clone(), chip.clone(),
layouter.namespace(|| "mul by zero"), layouter.namespace(|| "mul by zero"),
base_val, base_val,
scalar_fixed, scalar_fixed_short,
result, result,
)?; )?;
} }
@ -225,16 +294,11 @@ pub mod tests {
sign = -sign; sign = -sign;
} }
let scalar_fixed_short = sign * &scalar_fixed_short; let scalar_fixed_short = sign * &scalar_fixed_short;
let (result, _) = value_commit_v.mul(
let result = {
let scalar_fixed_short = ScalarFixedShort::new(
chip.clone(),
layouter.namespace(|| "random short scalar"), layouter.namespace(|| "random short scalar"),
Some(scalar_fixed_short), Some(scalar_fixed_short),
)?; )?;
value_commit_v.mul(layouter.namespace(|| "random [a]B"), &scalar_fixed_short)?
};
constrain_equal( constrain_equal(
chip.clone(), chip.clone(),
layouter.namespace(|| "random [a]B"), layouter.namespace(|| "random [a]B"),
@ -247,15 +311,11 @@ pub mod tests {
// [2^64 - 1]B // [2^64 - 1]B
{ {
let scalar_fixed_short = pallas::Scalar::from_u64(0xFFFF_FFFF_FFFF_FFFFu64); let scalar_fixed_short = pallas::Scalar::from_u64(0xFFFF_FFFF_FFFF_FFFFu64);
let (result, _) = value_commit_v.mul(
let result = { layouter.namespace(|| "[2^64 - 1]B"),
let scalar_fixed_short = ScalarFixedShort::new(
chip.clone(),
layouter.namespace(|| "2^64 - 1"),
Some(scalar_fixed_short), Some(scalar_fixed_short),
)?; )?;
value_commit_v.mul(layouter.namespace(|| "[2^64 - 1]B"), &scalar_fixed_short)?
};
constrain_equal( constrain_equal(
chip.clone(), chip.clone(),
layouter.namespace(|| "[2^64 - 1]B"), layouter.namespace(|| "[2^64 - 1]B"),
@ -268,15 +328,11 @@ pub mod tests {
// [-(2^64 - 1)]B // [-(2^64 - 1)]B
{ {
let scalar_fixed_short = -pallas::Scalar::from_u64(0xFFFF_FFFF_FFFF_FFFFu64); let scalar_fixed_short = -pallas::Scalar::from_u64(0xFFFF_FFFF_FFFF_FFFFu64);
let (result, _) = value_commit_v.mul(
let result = { layouter.namespace(|| "-[2^64 - 1]B"),
let scalar_fixed_short = ScalarFixedShort::new(
chip.clone(),
layouter.namespace(|| "-(2^64 - 1)"),
Some(scalar_fixed_short), Some(scalar_fixed_short),
)?; )?;
value_commit_v.mul(layouter.namespace(|| "[-(2^64 - 1)]B"), &scalar_fixed_short)?
};
constrain_equal( constrain_equal(
chip.clone(), chip.clone(),
layouter.namespace(|| "[-2^64 - 1]B"), layouter.namespace(|| "[-2^64 - 1]B"),
@ -292,17 +348,11 @@ pub mod tests {
{ {
let scalar_fixed_short = pallas::Scalar::from_u64(0xB6DB_6DB6_DB6D_B6DCu64); let scalar_fixed_short = pallas::Scalar::from_u64(0xB6DB_6DB6_DB6D_B6DCu64);
let result = { let (result, _) = value_commit_v.mul(
let scalar_fixed_short = ScalarFixedShort::new(
chip.clone(),
layouter.namespace(|| "mul with double"), layouter.namespace(|| "mul with double"),
Some(scalar_fixed_short), Some(scalar_fixed_short),
)?; )?;
value_commit_v.mul(
layouter.namespace(|| "mul with double"),
&scalar_fixed_short,
)?
};
constrain_equal( constrain_equal(
chip, chip,
layouter.namespace(|| "mul with double"), layouter.namespace(|| "mul with double"),

View File

@ -1,96 +0,0 @@
use super::{CellValue, EccConfig, Var};
use crate::{
circuit::gadget::utilities::range_check,
constants::{self, util},
};
use arrayvec::ArrayVec;
use halo2::{
circuit::Region,
plonk::{Advice, Column, ConstraintSystem, Error, Permutation, 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>,
perm: Permutation,
}
impl From<&EccConfig> for Config {
fn from(ecc_config: &EccConfig) -> Self {
Self {
q_scalar_fixed: ecc_config.q_scalar_fixed,
window: ecc_config.advices[9],
perm: ecc_config.perm.clone(),
}
}
}
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());
// Constrain each window to a 3-bit value:
// 1 * (window - 0) * (window - 1) * ... * (window - 7)
vec![q_scalar_fixed * range_check(window, constants::H)]
});
}
/// Witnesses the given scalar as `NUM_WINDOWS` 3-bit windows.
///
/// The scalar is allowed to be non-canonical.
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> {
// 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_word::<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)
}
}

View File

@ -1,27 +0,0 @@
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 })
}
}

View File

@ -1,98 +0,0 @@
use super::super::{CellValue, EccConfig, EccScalarFixedShort, Var};
use crate::constants::{L_VALUE, NUM_WINDOWS_SHORT};
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.
// Check that last window is either 0 or 1.
meta.create_gate("Check sign and last window", |meta| {
let q_scalar_fixed_short = meta.query_selector(self.q_scalar_fixed_short);
let last_window = meta.query_advice(self.super_config.window, Rotation::prev());
let sign = meta.query_advice(self.super_config.window, Rotation::cur());
let one = Expression::Constant(pallas::Base::one());
let last_window_check = last_window.clone() * (one.clone() - last_window);
let sign_check = sign.clone() * sign - one;
vec![
q_scalar_fixed_short.clone() * last_window_check,
q_scalar_fixed_short * sign_check,
]
});
}
}
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> {
// 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,
offset + NUM_WINDOWS_SHORT,
|| sign.ok_or(Error::SynthesisError),
)?;
Ok(EccScalarFixedShort {
magnitude,
sign: CellValue::<pallas::Base>::new(sign_cell, sign),
windows,
})
}
}