mirror of https://github.com/zcash/halo2.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.
|
//! 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)| {
|
||||||
chip: self.chip.clone(),
|
(
|
||||||
inner,
|
Point {
|
||||||
|
chip: self.chip.clone(),
|
||||||
|
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)| {
|
||||||
chip: self.chip.clone(),
|
(
|
||||||
inner,
|
Point {
|
||||||
|
chip: self.chip.clone(),
|
||||||
|
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)| {
|
||||||
chip: self.chip.clone(),
|
(
|
||||||
inner,
|
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;
|
||||||
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,
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
|
@ -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"),
|
||||||
|
|
|
@ -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(
|
||||||
|
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(
|
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(
|
||||||
|
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(
|
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(
|
||||||
|
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(
|
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(
|
layouter.namespace(|| "mul with double"),
|
||||||
chip.clone(),
|
Some(scalar_fixed_short),
|
||||||
layouter.namespace(|| "mul with double"),
|
)?;
|
||||||
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"),
|
||||||
|
|
|
@ -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