mirror of https://github.com/zcash/orchard.git
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:
parent
32f28ed4b0
commit
426f954b1d
|
@ -1,10 +1,9 @@
|
|||
//! Gadgets for elliptic curve operations.
|
||||
|
||||
use ff::Field;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use halo2::{
|
||||
arithmetic::{CurveAffine, FieldExt},
|
||||
arithmetic::CurveAffine,
|
||||
circuit::{Chip, Layouter},
|
||||
plonk::Error,
|
||||
};
|
||||
|
@ -53,30 +52,6 @@ pub trait EccInstructions<C: CurveAffine>: Chip<C::Base> + UtilitiesInstructions
|
|||
b: &Self::Point,
|
||||
) -> 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.
|
||||
/// This maps the identity to (0, 0) in affine coordinates.
|
||||
fn witness_point(
|
||||
|
@ -111,25 +86,25 @@ pub trait EccInstructions<C: CurveAffine>: Chip<C::Base> + UtilitiesInstructions
|
|||
fn mul(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<C::Base>,
|
||||
scalar: &Self::ScalarVar,
|
||||
scalar: &Self::Var,
|
||||
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`.
|
||||
fn mul_fixed(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<C::Base>,
|
||||
scalar: &Self::ScalarFixed,
|
||||
scalar: Option<C::Scalar>,
|
||||
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`.
|
||||
fn mul_fixed_short(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<C::Base>,
|
||||
scalar: &Self::ScalarFixedShort,
|
||||
scalar: Option<C::Scalar>,
|
||||
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.
|
||||
/// 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,
|
||||
}
|
||||
|
||||
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.
|
||||
#[derive(Debug)]
|
||||
pub struct ScalarFixed<C: CurveAffine, EccChip>
|
||||
|
@ -181,21 +144,6 @@ where
|
|||
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.
|
||||
#[derive(Debug)]
|
||||
pub struct ScalarFixedShort<C: CurveAffine, EccChip>
|
||||
|
@ -206,39 +154,6 @@ where
|
|||
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.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
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(
|
||||
&self,
|
||||
mut layouter: impl Layouter<C::Base>,
|
||||
by: &ScalarVar<C, EccChip>,
|
||||
) -> Result<Self, Error> {
|
||||
assert_eq!(self.chip, by.chip);
|
||||
by: &EccChip::Var,
|
||||
) -> Result<(Self, ScalarVar<C, EccChip>), Error> {
|
||||
self.chip
|
||||
.mul(&mut layouter, &by.inner, &self.inner)
|
||||
.map(|inner| Point {
|
||||
chip: self.chip.clone(),
|
||||
inner,
|
||||
.mul(&mut layouter, by, &self.inner)
|
||||
.map(|(point, scalar)| {
|
||||
(
|
||||
Point {
|
||||
chip: self.chip.clone(),
|
||||
inner: point,
|
||||
},
|
||||
ScalarVar {
|
||||
chip: self.chip.clone(),
|
||||
inner: scalar,
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -353,14 +275,21 @@ where
|
|||
pub fn mul(
|
||||
&self,
|
||||
mut layouter: impl Layouter<C::Base>,
|
||||
by: &ScalarFixed<C, EccChip>,
|
||||
) -> Result<Point<C, EccChip>, Error> {
|
||||
assert_eq!(self.chip, by.chip);
|
||||
by: Option<C::Scalar>,
|
||||
) -> Result<(Point<C, EccChip>, ScalarFixed<C, EccChip>), Error> {
|
||||
self.chip
|
||||
.mul_fixed(&mut layouter, &by.inner, &self.inner)
|
||||
.map(|inner| Point {
|
||||
chip: self.chip.clone(),
|
||||
inner,
|
||||
.mul_fixed(&mut layouter, by, &self.inner)
|
||||
.map(|(point, scalar)| {
|
||||
(
|
||||
Point {
|
||||
chip: self.chip.clone(),
|
||||
inner: point,
|
||||
},
|
||||
ScalarFixed {
|
||||
chip: self.chip.clone(),
|
||||
inner: scalar,
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -404,14 +333,21 @@ where
|
|||
pub fn mul(
|
||||
&self,
|
||||
mut layouter: impl Layouter<C::Base>,
|
||||
by: &ScalarFixedShort<C, EccChip>,
|
||||
) -> Result<Point<C, EccChip>, Error> {
|
||||
assert_eq!(self.chip, by.chip);
|
||||
by: Option<C::Scalar>,
|
||||
) -> Result<(Point<C, EccChip>, ScalarFixedShort<C, EccChip>), Error> {
|
||||
self.chip
|
||||
.mul_fixed_short(&mut layouter, &by.inner, &self.inner)
|
||||
.map(|inner| Point {
|
||||
chip: self.chip.clone(),
|
||||
inner,
|
||||
.mul_fixed_short(&mut layouter, by, &self.inner)
|
||||
.map(|(point, scalar)| {
|
||||
(
|
||||
Point {
|
||||
chip: self.chip.clone(),
|
||||
inner: point,
|
||||
},
|
||||
ScalarFixedShort {
|
||||
chip: self.chip.clone(),
|
||||
inner: scalar,
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ pub(super) mod add_incomplete;
|
|||
pub(super) mod mul;
|
||||
pub(super) mod mul_fixed;
|
||||
pub(super) mod witness_point;
|
||||
pub(super) mod witness_scalar_fixed;
|
||||
|
||||
/// A curve point represented in affine (x, y) coordinates. Each coordinate is
|
||||
/// assigned to a cell.
|
||||
|
@ -248,30 +247,15 @@ impl EccChip {
|
|||
mul_config.create_gate(meta);
|
||||
}
|
||||
|
||||
// Create witness scalar_fixed gate that applies to both full-width and
|
||||
// short scalars
|
||||
// Create gate that is only used in full-width fixed-base scalar mul.
|
||||
{
|
||||
let config: witness_scalar_fixed::Config = (&config).into();
|
||||
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);
|
||||
let mul_fixed_full_config: mul_fixed::full_width::Config = (&config).into();
|
||||
mul_fixed_full_config.create_gate(meta);
|
||||
}
|
||||
|
||||
// Create gate that is only used in short fixed-base scalar mul.
|
||||
{
|
||||
let short_config: mul_fixed::short::Config<{ constants::NUM_WINDOWS_SHORT }> =
|
||||
(&config).into();
|
||||
let short_config: mul_fixed::short::Config = (&config).into();
|
||||
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.
|
||||
/// 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)
|
||||
|
@ -350,7 +323,7 @@ impl EccBaseFieldElemFixed {
|
|||
impl EccInstructions<pallas::Affine> for EccChip {
|
||||
type ScalarFixed = EccScalarFixed;
|
||||
type ScalarFixedShort = EccScalarFixedShort;
|
||||
type ScalarVar = EccScalarVar;
|
||||
type ScalarVar = CellValue<pallas::Base>;
|
||||
type Point = EccPoint;
|
||||
type X = CellValue<pallas::Base>;
|
||||
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(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<pallas::Base>,
|
||||
|
@ -463,9 +392,9 @@ impl EccInstructions<pallas::Affine> for EccChip {
|
|||
fn mul(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<pallas::Base>,
|
||||
scalar: &Self::ScalarVar,
|
||||
scalar: &Self::Var,
|
||||
base: &Self::Point,
|
||||
) -> Result<Self::Point, Error> {
|
||||
) -> Result<(Self::Point, Self::ScalarVar), Error> {
|
||||
let config: mul::Config = self.config().into();
|
||||
config.assign(
|
||||
layouter.namespace(|| "variable-base scalar mul"),
|
||||
|
@ -477,11 +406,10 @@ impl EccInstructions<pallas::Affine> for EccChip {
|
|||
fn mul_fixed(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<pallas::Base>,
|
||||
scalar: &Self::ScalarFixed,
|
||||
scalar: Option<pallas::Scalar>,
|
||||
base: &Self::FixedPoints,
|
||||
) -> Result<Self::Point, Error> {
|
||||
let config: mul_fixed::full_width::Config<{ constants::NUM_WINDOWS }> =
|
||||
self.config().into();
|
||||
) -> Result<(Self::Point, Self::ScalarFixed), Error> {
|
||||
let config: mul_fixed::full_width::Config = self.config().into();
|
||||
config.assign(
|
||||
layouter.namespace(|| format!("fixed-base mul of {:?}", base)),
|
||||
scalar,
|
||||
|
@ -492,11 +420,10 @@ impl EccInstructions<pallas::Affine> for EccChip {
|
|||
fn mul_fixed_short(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<pallas::Base>,
|
||||
scalar: &Self::ScalarFixedShort,
|
||||
scalar: Option<pallas::Scalar>,
|
||||
base: &Self::FixedPointsShort,
|
||||
) -> Result<Self::Point, Error> {
|
||||
let config: mul_fixed::short::Config<{ constants::NUM_WINDOWS_SHORT }> =
|
||||
self.config().into();
|
||||
) -> Result<(Self::Point, Self::ScalarFixedShort), Error> {
|
||||
let config: mul_fixed::short::Config = self.config().into();
|
||||
config.assign(
|
||||
layouter.namespace(|| format!("short fixed-base mul of {:?}", base)),
|
||||
scalar,
|
||||
|
|
|
@ -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 std::ops::{Deref, Range};
|
||||
|
||||
|
@ -137,9 +137,9 @@ impl Config {
|
|||
pub(super) fn assign(
|
||||
&self,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
alpha: EccScalarVar,
|
||||
alpha: CellValue<pallas::Base>,
|
||||
base: &EccPoint,
|
||||
) -> Result<EccPoint, Error> {
|
||||
) -> Result<(EccPoint, CellValue<pallas::Base>), Error> {
|
||||
let (result, zs): (EccPoint, Vec<Z<pallas::Base>>) = layouter.assign_region(
|
||||
|| "variable-base scalar mul",
|
||||
|mut region| {
|
||||
|
@ -264,7 +264,7 @@ impl Config {
|
|||
self.overflow_config
|
||||
.overflow_check(layouter.namespace(|| "overflow check"), alpha, &zs)?;
|
||||
|
||||
Ok(result)
|
||||
Ok((result, alpha))
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
pub mod tests {
|
||||
use group::Curve;
|
||||
use halo2::{circuit::Layouter, plonk::Error};
|
||||
use halo2::{
|
||||
circuit::{Chip, Layouter},
|
||||
plonk::Error,
|
||||
};
|
||||
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,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
zero: &Point<pallas::Affine, EccChip>,
|
||||
p: &Point<pallas::Affine, EccChip>,
|
||||
p_val: pallas::Affine,
|
||||
) -> Result<(), Error> {
|
||||
let column = chip.config().advices[0];
|
||||
|
||||
fn constrain_equal<
|
||||
EccChip: EccInstructions<pallas::Affine> + Clone + Eq + std::fmt::Debug,
|
||||
>(
|
||||
|
@ -483,10 +491,10 @@ pub mod tests {
|
|||
// [a]B
|
||||
{
|
||||
let scalar_val = pallas::Base::rand();
|
||||
let result = {
|
||||
let scalar = ScalarVar::new(
|
||||
chip.clone(),
|
||||
let (result, _) = {
|
||||
let scalar = chip.load_private(
|
||||
layouter.namespace(|| "random scalar"),
|
||||
column,
|
||||
Some(scalar_val),
|
||||
)?;
|
||||
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.
|
||||
{
|
||||
let scalar_val = pallas::Base::rand();
|
||||
let scalar = ScalarVar::new(
|
||||
chip.clone(),
|
||||
let scalar = chip.load_private(
|
||||
layouter.namespace(|| "random scalar"),
|
||||
column,
|
||||
Some(scalar_val),
|
||||
)?;
|
||||
zero.mul(layouter.namespace(|| "[a]𝒪"), &scalar)
|
||||
|
@ -517,12 +525,9 @@ pub mod tests {
|
|||
// uses complete addition for the final bits of the scalar.
|
||||
{
|
||||
let scalar_val = pallas::Base::zero();
|
||||
let result = {
|
||||
let scalar = ScalarVar::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "zero"),
|
||||
Some(scalar_val),
|
||||
)?;
|
||||
let (result, _) = {
|
||||
let scalar =
|
||||
chip.load_private(layouter.namespace(|| "zero"), column, Some(scalar_val))?;
|
||||
p.mul(layouter.namespace(|| "[0]B"), &scalar)?
|
||||
};
|
||||
constrain_equal(
|
||||
|
@ -537,9 +542,9 @@ pub mod tests {
|
|||
// [-1]B (the largest possible base field element)
|
||||
{
|
||||
let scalar_val = -pallas::Base::one();
|
||||
let result = {
|
||||
let (result, _) = {
|
||||
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)?
|
||||
};
|
||||
constrain_equal(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use super::super::{copy, CellValue, EccConfig, EccScalarVar, Var};
|
||||
use super::super::{copy, CellValue, EccConfig, Var};
|
||||
use super::Z;
|
||||
use crate::{
|
||||
circuit::gadget::utilities::lookup_range_check::LookupRangeCheckConfig, constants::T_Q,
|
||||
|
@ -95,7 +95,7 @@ impl Config {
|
|||
pub(super) fn overflow_check(
|
||||
&self,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
alpha: EccScalarVar,
|
||||
alpha: CellValue<pallas::Base>,
|
||||
zs: &[Z<pallas::Base>], // [z_0, z_1, ..., z_{254}, z_{255}]
|
||||
) -> Result<(), Error> {
|
||||
// s = alpha + k_254 ⋅ 2^130 is witnessed here, and then copied into
|
||||
|
@ -189,7 +189,7 @@ impl Config {
|
|||
|| "copy original alpha",
|
||||
self.advices[1],
|
||||
offset + 1,
|
||||
&*alpha,
|
||||
&alpha,
|
||||
&self.perm,
|
||||
)?;
|
||||
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
use super::{
|
||||
add, add_incomplete, copy, CellValue, EccBaseFieldElemFixed, EccConfig, EccPoint,
|
||||
EccScalarFixed, EccScalarFixedShort, Var,
|
||||
add, add_incomplete, CellValue, EccBaseFieldElemFixed, EccConfig, EccPoint, EccScalarFixed,
|
||||
EccScalarFixedShort, Var,
|
||||
};
|
||||
use crate::constants::{
|
||||
self,
|
||||
load::{OrchardFixedBase, OrchardFixedBasesFull, ValueCommitV, WindowUs},
|
||||
util,
|
||||
};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use group::Curve;
|
||||
use halo2::{
|
||||
circuit::Region,
|
||||
plonk::{
|
||||
Advice, Column, ConstraintSystem, Error, Expression, Fixed, Permutation, Selector,
|
||||
VirtualCells,
|
||||
},
|
||||
plonk::{Advice, Column, Error, Expression, Fixed, Permutation, Selector, VirtualCells},
|
||||
poly::Rotation,
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
|
@ -72,6 +71,7 @@ impl OrchardFixedBases {
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct Config<const NUM_WINDOWS: usize> {
|
||||
q_mul_fixed: Selector,
|
||||
q_scalar_fixed: Selector,
|
||||
// The fixed Lagrange interpolation coefficients for `x_p`.
|
||||
lagrange_coeffs: [Column<Fixed>; constants::H],
|
||||
// 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 {
|
||||
let config = Self {
|
||||
q_mul_fixed: ecc_config.q_mul_fixed,
|
||||
q_scalar_fixed: ecc_config.q_scalar_fixed,
|
||||
lagrange_coeffs: ecc_config.lagrange_coeffs,
|
||||
fixed_z: ecc_config.fixed_z,
|
||||
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> {
|
||||
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)]
|
||||
fn coords_check(
|
||||
&self,
|
||||
|
@ -288,25 +278,53 @@ impl<const NUM_WINDOWS: usize> Config<NUM_WINDOWS> {
|
|||
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,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
scalar: Option<pallas::Scalar>,
|
||||
offset: usize,
|
||||
scalar: &ScalarFixed,
|
||||
) -> Result<(), Error> {
|
||||
// Copy the scalar decomposition (`k`-bit windows)
|
||||
for (window_idx, window) in scalar.windows().iter().enumerate() {
|
||||
copy(
|
||||
region,
|
||||
|| format!("k[{:?}]", window),
|
||||
self.window,
|
||||
window_idx + offset,
|
||||
window,
|
||||
&self.perm,
|
||||
)?;
|
||||
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)?;
|
||||
}
|
||||
|
||||
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(
|
||||
|
|
|
@ -490,6 +490,7 @@ pub mod tests {
|
|||
base_val: pallas::Affine,
|
||||
) -> Result<(), Error> {
|
||||
let column = chip.config().advices[0];
|
||||
|
||||
fn constrain_equal(
|
||||
chip: EccChip,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
|
|
|
@ -1,38 +1,90 @@
|
|||
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;
|
||||
|
||||
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 {
|
||||
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(
|
||||
&self,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
scalar: &EccScalarFixed,
|
||||
scalar: Option<pallas::Scalar>,
|
||||
base: OrchardFixedBasesFull,
|
||||
) -> Result<EccPoint, Error> {
|
||||
let (acc, mul_b) = layouter.assign_region(
|
||||
) -> Result<(EccPoint, EccScalarFixed), Error> {
|
||||
let (scalar, acc, mul_b) = layouter.assign_region(
|
||||
|| "Full-width fixed-base mul (incomplete addition)",
|
||||
|mut region| {
|
||||
let offset = 0;
|
||||
|
||||
// Copy the scalar decomposition
|
||||
self.0.copy_scalar(&mut region, offset, &scalar.into())?;
|
||||
let scalar = self.witness(&mut region, offset, scalar)?;
|
||||
|
||||
self.0.assign_region_inner(
|
||||
let (acc, mul_b) = self.super_config.assign_region_inner(
|
||||
&mut region,
|
||||
offset,
|
||||
&scalar.into(),
|
||||
&(&scalar).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(
|
||||
|| "Full-width fixed-base mul (last window, complete addition)",
|
||||
|mut region| {
|
||||
self.0
|
||||
self.super_config
|
||||
.add_config
|
||||
.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::{
|
||||
chip::{EccChip, OrchardFixedBasesFull},
|
||||
FixedPoint, Point, ScalarFixed,
|
||||
FixedPoint, Point,
|
||||
};
|
||||
use crate::constants;
|
||||
|
||||
|
@ -154,15 +206,7 @@ pub mod tests {
|
|||
{
|
||||
let scalar_fixed = pallas::Scalar::rand();
|
||||
|
||||
let result = {
|
||||
let scalar_fixed = ScalarFixed::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "random scalar"),
|
||||
Some(scalar_fixed),
|
||||
)?;
|
||||
base.mul(layouter.namespace(|| "random [a]B"), &scalar_fixed)?
|
||||
};
|
||||
|
||||
let (result, _) = base.mul(layouter.namespace(|| "random [a]B"), Some(scalar_fixed))?;
|
||||
constrain_equal(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "random [a]B"),
|
||||
|
@ -183,14 +227,8 @@ pub mod tests {
|
|||
.fold(pallas::Scalar::zero(), |acc, c| {
|
||||
acc * &h + &pallas::Scalar::from_u64(c.to_digit(8).unwrap().into())
|
||||
});
|
||||
let result = {
|
||||
let scalar_fixed = ScalarFixed::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "mul with double"),
|
||||
Some(scalar_fixed),
|
||||
)?;
|
||||
base.mul(layouter.namespace(|| "mul with double"), &scalar_fixed)?
|
||||
};
|
||||
let (result, _) =
|
||||
base.mul(layouter.namespace(|| "mul with double"), Some(scalar_fixed))?;
|
||||
|
||||
constrain_equal(
|
||||
chip.clone(),
|
||||
|
@ -205,14 +243,7 @@ pub mod tests {
|
|||
// on the last step.
|
||||
{
|
||||
let scalar_fixed = pallas::Scalar::zero();
|
||||
let result = {
|
||||
let scalar_fixed = ScalarFixed::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "zero"),
|
||||
Some(scalar_fixed),
|
||||
)?;
|
||||
base.mul(layouter.namespace(|| "mul by zero"), &scalar_fixed)?
|
||||
};
|
||||
let (result, _) = base.mul(layouter.namespace(|| "mul by zero"), Some(scalar_fixed))?;
|
||||
constrain_equal(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "mul by zero"),
|
||||
|
@ -225,14 +256,7 @@ pub mod tests {
|
|||
// [-1]B is the largest scalar field element.
|
||||
{
|
||||
let scalar_fixed = -pallas::Scalar::one();
|
||||
let result = {
|
||||
let scalar_fixed = ScalarFixed::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "-1"),
|
||||
Some(scalar_fixed),
|
||||
)?;
|
||||
base.mul(layouter.namespace(|| "mul by -1"), &scalar_fixed)?
|
||||
};
|
||||
let (result, _) = base.mul(layouter.namespace(|| "mul by -1"), Some(scalar_fixed))?;
|
||||
constrain_equal(
|
||||
chip,
|
||||
layouter.namespace(|| "mul by -1"),
|
||||
|
|
|
@ -1,34 +1,54 @@
|
|||
use std::array;
|
||||
|
||||
use super::super::{copy, CellValue, EccConfig, EccPoint, EccScalarFixedShort, Var};
|
||||
use crate::constants::ValueCommitV;
|
||||
use crate::constants::{ValueCommitV, L_VALUE, NUM_WINDOWS_SHORT};
|
||||
|
||||
use halo2::{
|
||||
circuit::Layouter,
|
||||
plonk::{ConstraintSystem, Error, Selector},
|
||||
circuit::{Layouter, Region},
|
||||
plonk::{ConstraintSystem, Error, Expression, Selector},
|
||||
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.
|
||||
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 {
|
||||
Self {
|
||||
q_mul_fixed_short: config.q_mul_fixed_short,
|
||||
q_scalar_fixed_short: config.q_scalar_fixed_short,
|
||||
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.
|
||||
// Here, we add some new constraints specific to the short signed case.
|
||||
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| {
|
||||
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());
|
||||
|
@ -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(
|
||||
&self,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
scalar: &EccScalarFixedShort,
|
||||
scalar: Option<pallas::Scalar>,
|
||||
base: &ValueCommitV,
|
||||
) -> Result<EccPoint, Error> {
|
||||
let (acc, mul_b) = layouter.assign_region(
|
||||
) -> Result<(EccPoint, EccScalarFixedShort), Error> {
|
||||
let (scalar, acc, mul_b) = layouter.assign_region(
|
||||
|| "Short fixed-base mul (incomplete addition)",
|
||||
|mut region| {
|
||||
let offset = 0;
|
||||
|
||||
// Copy the scalar decomposition
|
||||
self.super_config
|
||||
.copy_scalar(&mut region, offset, &scalar.into())?;
|
||||
let scalar = self.witness(&mut region, offset, scalar)?;
|
||||
|
||||
self.super_config.assign_region_inner(
|
||||
let (acc, mul_b) = self.super_config.assign_region_inner(
|
||||
&mut region,
|
||||
offset,
|
||||
&scalar.into(),
|
||||
&(&scalar).into(),
|
||||
base.clone().into(),
|
||||
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 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;
|
||||
|
||||
#[allow(clippy::op_ref)]
|
||||
|
@ -199,20 +271,17 @@ pub mod tests {
|
|||
// [0]B should return (0,0) since it uses complete addition
|
||||
// on the last step.
|
||||
{
|
||||
let scalar_fixed = pallas::Scalar::zero();
|
||||
let result = {
|
||||
let scalar_fixed = ScalarFixedShort::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "zero"),
|
||||
Some(scalar_fixed),
|
||||
)?;
|
||||
value_commit_v.mul(layouter.namespace(|| "mul by zero"), &scalar_fixed)?
|
||||
};
|
||||
let scalar_fixed_short = pallas::Scalar::zero();
|
||||
let (result, _) = value_commit_v.mul(
|
||||
layouter.namespace(|| "mul by zero"),
|
||||
Some(scalar_fixed_short),
|
||||
)?;
|
||||
|
||||
constrain_equal(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "mul by zero"),
|
||||
base_val,
|
||||
scalar_fixed,
|
||||
scalar_fixed_short,
|
||||
result,
|
||||
)?;
|
||||
}
|
||||
|
@ -225,16 +294,11 @@ pub mod tests {
|
|||
sign = -sign;
|
||||
}
|
||||
let scalar_fixed_short = sign * &scalar_fixed_short;
|
||||
let (result, _) = value_commit_v.mul(
|
||||
layouter.namespace(|| "random short scalar"),
|
||||
Some(scalar_fixed_short),
|
||||
)?;
|
||||
|
||||
let result = {
|
||||
let scalar_fixed_short = ScalarFixedShort::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "random short scalar"),
|
||||
Some(scalar_fixed_short),
|
||||
)?;
|
||||
|
||||
value_commit_v.mul(layouter.namespace(|| "random [a]B"), &scalar_fixed_short)?
|
||||
};
|
||||
constrain_equal(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "random [a]B"),
|
||||
|
@ -247,15 +311,11 @@ pub mod tests {
|
|||
// [2^64 - 1]B
|
||||
{
|
||||
let scalar_fixed_short = pallas::Scalar::from_u64(0xFFFF_FFFF_FFFF_FFFFu64);
|
||||
let (result, _) = value_commit_v.mul(
|
||||
layouter.namespace(|| "[2^64 - 1]B"),
|
||||
Some(scalar_fixed_short),
|
||||
)?;
|
||||
|
||||
let result = {
|
||||
let scalar_fixed_short = ScalarFixedShort::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "2^64 - 1"),
|
||||
Some(scalar_fixed_short),
|
||||
)?;
|
||||
value_commit_v.mul(layouter.namespace(|| "[2^64 - 1]B"), &scalar_fixed_short)?
|
||||
};
|
||||
constrain_equal(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "[2^64 - 1]B"),
|
||||
|
@ -268,15 +328,11 @@ pub mod tests {
|
|||
// [-(2^64 - 1)]B
|
||||
{
|
||||
let scalar_fixed_short = -pallas::Scalar::from_u64(0xFFFF_FFFF_FFFF_FFFFu64);
|
||||
let (result, _) = value_commit_v.mul(
|
||||
layouter.namespace(|| "-[2^64 - 1]B"),
|
||||
Some(scalar_fixed_short),
|
||||
)?;
|
||||
|
||||
let result = {
|
||||
let scalar_fixed_short = ScalarFixedShort::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "-(2^64 - 1)"),
|
||||
Some(scalar_fixed_short),
|
||||
)?;
|
||||
value_commit_v.mul(layouter.namespace(|| "[-(2^64 - 1)]B"), &scalar_fixed_short)?
|
||||
};
|
||||
constrain_equal(
|
||||
chip.clone(),
|
||||
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 result = {
|
||||
let scalar_fixed_short = ScalarFixedShort::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "mul with double"),
|
||||
Some(scalar_fixed_short),
|
||||
)?;
|
||||
value_commit_v.mul(
|
||||
layouter.namespace(|| "mul with double"),
|
||||
&scalar_fixed_short,
|
||||
)?
|
||||
};
|
||||
let (result, _) = value_commit_v.mul(
|
||||
layouter.namespace(|| "mul with double"),
|
||||
Some(scalar_fixed_short),
|
||||
)?;
|
||||
|
||||
constrain_equal(
|
||||
chip,
|
||||
layouter.namespace(|| "mul with double"),
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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 })
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue