mirror of https://github.com/zcash/halo2.git
mul_fixed::short: Copy (magnitude, sign) instead of witnessing Scalar.
In the Orchard circuit, the short signed scalar is v_old - v_new, which will be witnessed as two cells: a 64-bit magnitude, and a sign that is +/- 1.
This commit is contained in:
parent
426f954b1d
commit
a8bd2d6abf
|
@ -102,7 +102,7 @@ pub trait EccInstructions<C: CurveAffine>: Chip<C::Base> + UtilitiesInstructions
|
||||||
fn mul_fixed_short(
|
fn mul_fixed_short(
|
||||||
&self,
|
&self,
|
||||||
layouter: &mut impl Layouter<C::Base>,
|
layouter: &mut impl Layouter<C::Base>,
|
||||||
scalar: Option<C::Scalar>,
|
magnitude_sign: (Self::Var, Self::Var),
|
||||||
base: &Self::FixedPointsShort,
|
base: &Self::FixedPointsShort,
|
||||||
) -> Result<(Self::Point, Self::ScalarFixedShort), Error>;
|
) -> Result<(Self::Point, Self::ScalarFixedShort), Error>;
|
||||||
|
|
||||||
|
@ -271,6 +271,7 @@ impl<C: CurveAffine, EccChip> FixedPoint<C, EccChip>
|
||||||
where
|
where
|
||||||
EccChip: EccInstructions<C> + Clone + Debug + Eq,
|
EccChip: EccInstructions<C> + Clone + Debug + Eq,
|
||||||
{
|
{
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
/// Returns `[by] self`.
|
/// Returns `[by] self`.
|
||||||
pub fn mul(
|
pub fn mul(
|
||||||
&self,
|
&self,
|
||||||
|
@ -329,14 +330,15 @@ impl<C: CurveAffine, EccChip> FixedPointShort<C, EccChip>
|
||||||
where
|
where
|
||||||
EccChip: EccInstructions<C> + Clone + Debug + Eq,
|
EccChip: EccInstructions<C> + Clone + Debug + Eq,
|
||||||
{
|
{
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
/// Returns `[by] self`.
|
/// Returns `[by] self`.
|
||||||
pub fn mul(
|
pub fn mul(
|
||||||
&self,
|
&self,
|
||||||
mut layouter: impl Layouter<C::Base>,
|
mut layouter: impl Layouter<C::Base>,
|
||||||
by: Option<C::Scalar>,
|
magnitude_sign: (EccChip::Var, EccChip::Var),
|
||||||
) -> Result<(Point<C, EccChip>, ScalarFixedShort<C, EccChip>), Error> {
|
) -> Result<(Point<C, EccChip>, ScalarFixedShort<C, EccChip>), Error> {
|
||||||
self.chip
|
self.chip
|
||||||
.mul_fixed_short(&mut layouter, by, &self.inner)
|
.mul_fixed_short(&mut layouter, magnitude_sign, &self.inner)
|
||||||
.map(|(point, scalar)| {
|
.map(|(point, scalar)| {
|
||||||
(
|
(
|
||||||
Point {
|
Point {
|
||||||
|
|
|
@ -294,9 +294,9 @@ pub struct EccScalarFixed {
|
||||||
/// k_21 must be a single bit, i.e. 0 or 1.
|
/// k_21 must be a single bit, i.e. 0 or 1.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct EccScalarFixedShort {
|
pub struct EccScalarFixedShort {
|
||||||
magnitude: Option<pallas::Scalar>,
|
magnitude: CellValue<pallas::Base>,
|
||||||
sign: CellValue<pallas::Base>,
|
sign: CellValue<pallas::Base>,
|
||||||
windows: ArrayVec<CellValue<pallas::Base>, { constants::NUM_WINDOWS_SHORT }>,
|
running_sum: ArrayVec<CellValue<pallas::Base>, { constants::NUM_WINDOWS_SHORT }>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A base field element used for fixed-base scalar multiplication.
|
/// A base field element used for fixed-base scalar multiplication.
|
||||||
|
@ -420,13 +420,13 @@ 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: Option<pallas::Scalar>,
|
magnitude_sign: (CellValue<pallas::Base>, CellValue<pallas::Base>),
|
||||||
base: &Self::FixedPointsShort,
|
base: &Self::FixedPointsShort,
|
||||||
) -> Result<(Self::Point, Self::ScalarFixedShort), Error> {
|
) -> Result<(Self::Point, Self::ScalarFixedShort), Error> {
|
||||||
let config: mul_fixed::short::Config = self.config().into();
|
let config: mul_fixed::short::Config = 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,
|
magnitude_sign,
|
||||||
base,
|
base,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -510,22 +510,10 @@ impl From<&EccBaseFieldElemFixed> for ScalarFixed {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScalarFixed {
|
impl ScalarFixed {
|
||||||
fn windows(&self) -> &[CellValue<pallas::Base>] {
|
|
||||||
match self {
|
|
||||||
ScalarFixed::FullWidth(scalar) => &scalar.windows,
|
|
||||||
ScalarFixed::Short(scalar) => &scalar.windows,
|
|
||||||
_ => unreachable!("The base field element is not witnessed as windows."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The scalar decomposition was done in the base field. For computation
|
// The scalar decomposition was done in the base field. For computation
|
||||||
// outside the circuit, we now convert them back into the scalar field.
|
// outside the circuit, we now convert them back into the scalar field.
|
||||||
fn windows_field(&self) -> Vec<Option<pallas::Scalar>> {
|
fn windows_field(&self) -> Vec<Option<pallas::Scalar>> {
|
||||||
match self {
|
let running_sum_to_windows = |zs: Vec<CellValue<pallas::Base>>| {
|
||||||
Self::BaseFieldElem(scalar) => {
|
|
||||||
let mut zs = vec![scalar.base_field_elem];
|
|
||||||
zs.extend_from_slice(&scalar.running_sum);
|
|
||||||
|
|
||||||
(0..(zs.len() - 1))
|
(0..(zs.len() - 1))
|
||||||
.map(|idx| {
|
.map(|idx| {
|
||||||
let z_cur = zs[idx].value();
|
let z_cur = zs[idx].value();
|
||||||
|
@ -536,9 +524,20 @@ impl ScalarFixed {
|
||||||
word.map(|word| pallas::Scalar::from_bytes(&word.to_bytes()).unwrap())
|
word.map(|word| pallas::Scalar::from_bytes(&word.to_bytes()).unwrap())
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
|
};
|
||||||
|
match self {
|
||||||
|
Self::BaseFieldElem(scalar) => {
|
||||||
|
let mut zs = vec![scalar.base_field_elem];
|
||||||
|
zs.extend_from_slice(&scalar.running_sum);
|
||||||
|
running_sum_to_windows(zs)
|
||||||
}
|
}
|
||||||
_ => self
|
Self::Short(scalar) => {
|
||||||
.windows()
|
let mut zs = vec![scalar.magnitude];
|
||||||
|
zs.extend_from_slice(&scalar.running_sum);
|
||||||
|
running_sum_to_windows(zs)
|
||||||
|
}
|
||||||
|
Self::FullWidth(scalar) => scalar
|
||||||
|
.windows
|
||||||
.iter()
|
.iter()
|
||||||
.map(|bits| {
|
.map(|bits| {
|
||||||
bits.value()
|
bits.value()
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
use std::array;
|
use std::{array, convert::TryInto};
|
||||||
|
|
||||||
use super::super::{copy, CellValue, EccConfig, EccPoint, EccScalarFixedShort, Var};
|
use super::super::{EccConfig, EccPoint, EccScalarFixedShort};
|
||||||
use crate::constants::{ValueCommitV, L_VALUE, NUM_WINDOWS_SHORT};
|
use crate::{
|
||||||
|
circuit::gadget::utilities::{copy, decompose_running_sum::RunningSumConfig, CellValue, Var},
|
||||||
|
constants::{self, ValueCommitV, FIXED_BASE_WINDOW_SIZE, L_VALUE, NUM_WINDOWS_SHORT},
|
||||||
|
};
|
||||||
|
|
||||||
use halo2::{
|
use halo2::{
|
||||||
circuit::{Layouter, Region},
|
circuit::{Layouter, Region},
|
||||||
|
@ -13,7 +16,13 @@ use pasta_curves::{arithmetic::FieldExt, pallas};
|
||||||
pub struct Config {
|
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,
|
||||||
q_scalar_fixed_short: Selector,
|
q_mul_fixed_running_sum: Selector,
|
||||||
|
running_sum_config: RunningSumConfig<
|
||||||
|
pallas::Base,
|
||||||
|
{ L_VALUE },
|
||||||
|
{ FIXED_BASE_WINDOW_SIZE },
|
||||||
|
{ NUM_WINDOWS_SHORT },
|
||||||
|
>,
|
||||||
super_config: super::Config<NUM_WINDOWS_SHORT>,
|
super_config: super::Config<NUM_WINDOWS_SHORT>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,40 +30,45 @@ 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,
|
q_mul_fixed_running_sum: config.q_mul_fixed_running_sum,
|
||||||
|
running_sum_config: config.running_sum_short_config.clone(),
|
||||||
super_config: config.into(),
|
super_config: config.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
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>) {
|
pub(crate) fn create_gate(&self, meta: &mut ConstraintSystem<pallas::Base>) {
|
||||||
// Check that sign is either 1 or -1.
|
// Check that each window uses the correct y_p and interpolated x_p.
|
||||||
// Check that last window is either 0 or 1.
|
meta.create_gate("Coordinates check", |meta| {
|
||||||
meta.create_gate("Check sign and last window", |meta| {
|
let q_mul_fixed_running_sum = meta.query_selector(self.q_mul_fixed_running_sum);
|
||||||
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 z_cur = meta.query_advice(self.super_config.window, Rotation::cur());
|
||||||
|
let z_next = meta.query_advice(self.super_config.window, Rotation::next());
|
||||||
|
|
||||||
let last_window_check = last_window.clone() * (one.clone() - last_window);
|
// z_{i+1} = (z_i - a_i) / 2^3
|
||||||
let sign_check = sign.clone() * sign - one;
|
// => a_i = z_i - z_{i+1} * 2^3
|
||||||
|
let word = z_cur - z_next * pallas::Base::from_u64(constants::H as u64);
|
||||||
|
|
||||||
vec![
|
self.super_config
|
||||||
q_scalar_fixed_short.clone() * last_window_check,
|
.coords_check(meta, q_mul_fixed_running_sum, word)
|
||||||
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());
|
||||||
let y_a = meta.query_advice(self.super_config.add_config.y_qr, Rotation::cur());
|
let y_a = meta.query_advice(self.super_config.add_config.y_qr, Rotation::cur());
|
||||||
|
// z_21
|
||||||
|
let last_window = meta.query_advice(self.super_config.u, Rotation::cur());
|
||||||
let sign = meta.query_advice(self.super_config.window, Rotation::cur());
|
let sign = meta.query_advice(self.super_config.window, Rotation::cur());
|
||||||
|
|
||||||
|
let one = Expression::Constant(pallas::Base::one());
|
||||||
|
|
||||||
|
// Check that last window is either 0 or 1.
|
||||||
|
let last_window_check = last_window.clone() * (one.clone() - last_window);
|
||||||
|
// Check that sign is either 1 or -1.
|
||||||
|
let sign_check = sign.clone() * sign.clone() - one;
|
||||||
|
|
||||||
// `(x_a, y_a)` is the result of `[m]B`, where `m` is the magnitude.
|
// `(x_a, y_a)` is the result of `[m]B`, where `m` is the magnitude.
|
||||||
// We conditionally negate this result using `y_p = y_a * s`, where `s` is the sign.
|
// We conditionally negate this result using `y_p = y_a * s`, where `s` is the sign.
|
||||||
|
|
||||||
|
@ -66,66 +80,40 @@ impl Config {
|
||||||
// Check that the correct sign is witnessed s.t. sign * y_p = y_a
|
// Check that the correct sign is witnessed s.t. sign * y_p = y_a
|
||||||
let negation_check = sign * y_p - y_a;
|
let negation_check = sign * y_p - y_a;
|
||||||
|
|
||||||
array::IntoIter::new([y_check, negation_check])
|
array::IntoIter::new([
|
||||||
.map(move |poly| q_mul_fixed_short.clone() * poly)
|
("last_window_check", last_window_check),
|
||||||
|
("sign_check", sign_check),
|
||||||
|
("y_check", y_check),
|
||||||
|
("negation_check", negation_check),
|
||||||
|
])
|
||||||
|
.map(move |(name, poly)| (name, q_mul_fixed_short.clone() * poly))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn witness(
|
fn decompose(
|
||||||
&self,
|
&self,
|
||||||
region: &mut Region<'_, pallas::Base>,
|
region: &mut Region<'_, pallas::Base>,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
value: Option<pallas::Scalar>,
|
magnitude_sign: (CellValue<pallas::Base>, CellValue<pallas::Base>),
|
||||||
) -> Result<EccScalarFixedShort, Error> {
|
) -> Result<EccScalarFixedShort, Error> {
|
||||||
// Enable `q_scalar_fixed_short`
|
let (magnitude, sign) = magnitude_sign;
|
||||||
self.q_scalar_fixed_short
|
|
||||||
.enable(region, offset + NUM_WINDOWS_SHORT)?;
|
|
||||||
|
|
||||||
// Compute the scalar's sign and magnitude
|
// Decompose magnitude
|
||||||
let sign = value.map(|value| {
|
let (magnitude, running_sum) = self
|
||||||
// t = (p - 1)/2
|
.running_sum_config
|
||||||
let t = (pallas::Scalar::zero() - &pallas::Scalar::one()) * &pallas::Scalar::TWO_INV;
|
.copy_decompose(region, offset, magnitude, true)?;
|
||||||
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 {
|
Ok(EccScalarFixedShort {
|
||||||
magnitude,
|
magnitude,
|
||||||
sign: CellValue::<pallas::Base>::new(sign_cell, sign),
|
sign,
|
||||||
windows,
|
running_sum: (*running_sum).as_slice().try_into().unwrap(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assign(
|
pub fn assign(
|
||||||
&self,
|
&self,
|
||||||
mut layouter: impl Layouter<pallas::Base>,
|
mut layouter: impl Layouter<pallas::Base>,
|
||||||
scalar: Option<pallas::Scalar>,
|
magnitude_sign: (CellValue<pallas::Base>, CellValue<pallas::Base>),
|
||||||
base: &ValueCommitV,
|
base: &ValueCommitV,
|
||||||
) -> Result<(EccPoint, EccScalarFixedShort), Error> {
|
) -> Result<(EccPoint, EccScalarFixedShort), Error> {
|
||||||
let (scalar, acc, mul_b) = layouter.assign_region(
|
let (scalar, acc, mul_b) = layouter.assign_region(
|
||||||
|
@ -133,8 +121,8 @@ impl Config {
|
||||||
|mut region| {
|
|mut region| {
|
||||||
let offset = 0;
|
let offset = 0;
|
||||||
|
|
||||||
// Copy the scalar decomposition
|
// Decompose the scalar
|
||||||
let scalar = self.witness(&mut region, offset, scalar)?;
|
let scalar = self.decompose(&mut region, offset, magnitude_sign)?;
|
||||||
|
|
||||||
let (acc, mul_b) = self.super_config.assign_region_inner(
|
let (acc, mul_b) = self.super_config.assign_region_inner(
|
||||||
&mut region,
|
&mut region,
|
||||||
|
@ -148,6 +136,7 @@ impl Config {
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// Last window
|
||||||
let result = layouter.assign_region(
|
let result = layouter.assign_region(
|
||||||
|| "Short fixed-base mul (most significant word)",
|
|| "Short fixed-base mul (most significant word)",
|
||||||
|mut region| {
|
|mut region| {
|
||||||
|
@ -163,7 +152,7 @@ impl Config {
|
||||||
// Increase offset by 1 after complete addition
|
// Increase offset by 1 after complete addition
|
||||||
let offset = offset + 1;
|
let offset = offset + 1;
|
||||||
|
|
||||||
// Assign sign to `window` column
|
// Copy sign to `window` column
|
||||||
let sign = copy(
|
let sign = copy(
|
||||||
&mut region,
|
&mut region,
|
||||||
|| "sign",
|
|| "sign",
|
||||||
|
@ -173,6 +162,17 @@ impl Config {
|
||||||
&self.super_config.perm,
|
&self.super_config.perm,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// Copy last window to `u` column
|
||||||
|
let z_21 = scalar.running_sum[20];
|
||||||
|
copy(
|
||||||
|
&mut region,
|
||||||
|
|| "last_window",
|
||||||
|
self.super_config.u,
|
||||||
|
offset,
|
||||||
|
&z_21,
|
||||||
|
&self.super_config.perm,
|
||||||
|
)?;
|
||||||
|
|
||||||
// Conditionally negate `y`-coordinate
|
// Conditionally negate `y`-coordinate
|
||||||
let y_val = if let Some(sign) = sign.value() {
|
let y_val = if let Some(sign) = sign.value() {
|
||||||
if sign == -pallas::Base::one() {
|
if sign == -pallas::Base::one() {
|
||||||
|
@ -209,10 +209,16 @@ impl Config {
|
||||||
|
|
||||||
let base: super::OrchardFixedBases = base.clone().into();
|
let base: super::OrchardFixedBases = base.clone().into();
|
||||||
|
|
||||||
let scalar = scalar
|
let scalar =
|
||||||
|
scalar
|
||||||
.magnitude
|
.magnitude
|
||||||
|
.value()
|
||||||
.zip(scalar.sign.value())
|
.zip(scalar.sign.value())
|
||||||
.map(|(magnitude, sign)| {
|
.map(|(magnitude, sign)| {
|
||||||
|
// Move magnitude from base field into scalar field (which always fits
|
||||||
|
// for Pallas).
|
||||||
|
let magnitude = pallas::Scalar::from_bytes(&magnitude.to_bytes()).unwrap();
|
||||||
|
|
||||||
let sign = if sign == pallas::Base::one() {
|
let sign = if sign == pallas::Base::one() {
|
||||||
pallas::Scalar::one()
|
pallas::Scalar::one()
|
||||||
} else if sign == -pallas::Base::one() {
|
} else if sign == -pallas::Base::one() {
|
||||||
|
@ -237,10 +243,16 @@ impl Config {
|
||||||
#[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::{chip::EccChip, FixedPointShort, Point};
|
use crate::circuit::gadget::{
|
||||||
|
ecc::{chip::EccChip, FixedPointShort, Point},
|
||||||
|
utilities::{CellValue, UtilitiesInstructions},
|
||||||
|
};
|
||||||
use crate::constants::load::ValueCommitV;
|
use crate::constants::load::ValueCommitV;
|
||||||
|
|
||||||
#[allow(clippy::op_ref)]
|
#[allow(clippy::op_ref)]
|
||||||
|
@ -253,6 +265,20 @@ pub mod tests {
|
||||||
let base_val = value_commit_v.generator;
|
let base_val = value_commit_v.generator;
|
||||||
let value_commit_v = FixedPointShort::from_inner(chip.clone(), value_commit_v);
|
let value_commit_v = FixedPointShort::from_inner(chip.clone(), value_commit_v);
|
||||||
|
|
||||||
|
fn load_magnitude_sign(
|
||||||
|
chip: EccChip,
|
||||||
|
mut layouter: impl Layouter<pallas::Base>,
|
||||||
|
magnitude: pallas::Base,
|
||||||
|
sign: pallas::Base,
|
||||||
|
) -> Result<(CellValue<pallas::Base>, CellValue<pallas::Base>), Error> {
|
||||||
|
let column = chip.config().advices[0];
|
||||||
|
let magnitude =
|
||||||
|
chip.load_private(layouter.namespace(|| "magnitude"), column, Some(magnitude))?;
|
||||||
|
let sign = chip.load_private(layouter.namespace(|| "sign"), column, Some(sign))?;
|
||||||
|
|
||||||
|
Ok((magnitude, sign))
|
||||||
|
}
|
||||||
|
|
||||||
fn constrain_equal(
|
fn constrain_equal(
|
||||||
chip: EccChip,
|
chip: EccChip,
|
||||||
mut layouter: impl Layouter<pallas::Base>,
|
mut layouter: impl Layouter<pallas::Base>,
|
||||||
|
@ -268,96 +294,63 @@ pub mod tests {
|
||||||
result.constrain_equal(layouter.namespace(|| "constrain result"), &expected)
|
result.constrain_equal(layouter.namespace(|| "constrain result"), &expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
// [0]B should return (0,0) since it uses complete addition
|
let mut random_sign = pallas::Base::one();
|
||||||
// on the last step.
|
|
||||||
{
|
|
||||||
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_short,
|
|
||||||
result,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Random [a]B
|
|
||||||
{
|
|
||||||
let scalar_fixed_short = pallas::Scalar::from_u64(rand::random::<u64>());
|
|
||||||
let mut sign = pallas::Scalar::one();
|
|
||||||
if rand::random::<bool>() {
|
if rand::random::<bool>() {
|
||||||
sign = -sign;
|
random_sign = -random_sign;
|
||||||
}
|
}
|
||||||
let scalar_fixed_short = sign * &scalar_fixed_short;
|
let magnitude_signs = [
|
||||||
let (result, _) = value_commit_v.mul(
|
("mul by +zero", pallas::Base::zero(), pallas::Base::one()),
|
||||||
layouter.namespace(|| "random short scalar"),
|
("mul by -zero", pallas::Base::zero(), -pallas::Base::one()),
|
||||||
Some(scalar_fixed_short),
|
(
|
||||||
)?;
|
"random [a]B",
|
||||||
|
pallas::Base::from_u64(rand::random::<u64>()),
|
||||||
constrain_equal(
|
random_sign,
|
||||||
chip.clone(),
|
),
|
||||||
layouter.namespace(|| "random [a]B"),
|
(
|
||||||
base_val,
|
"[2^64 - 1]B",
|
||||||
scalar_fixed_short,
|
pallas::Base::from_u64(0xFFFF_FFFF_FFFF_FFFFu64),
|
||||||
result,
|
pallas::Base::one(),
|
||||||
)?;
|
),
|
||||||
}
|
(
|
||||||
|
"-[2^64 - 1]B",
|
||||||
// [2^64 - 1]B
|
pallas::Base::from_u64(0xFFFF_FFFF_FFFF_FFFFu64),
|
||||||
{
|
-pallas::Base::one(),
|
||||||
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),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
constrain_equal(
|
|
||||||
chip.clone(),
|
|
||||||
layouter.namespace(|| "[2^64 - 1]B"),
|
|
||||||
base_val,
|
|
||||||
scalar_fixed_short,
|
|
||||||
result,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// [-(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),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
constrain_equal(
|
|
||||||
chip.clone(),
|
|
||||||
layouter.namespace(|| "[-2^64 - 1]B"),
|
|
||||||
base_val,
|
|
||||||
scalar_fixed_short,
|
|
||||||
result,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// There is a single canonical sequence of window values for which a doubling occurs on the last step:
|
// There is a single canonical sequence of window values for which a doubling occurs on the last step:
|
||||||
// 1333333333333333333334 in octal.
|
// 1333333333333333333334 in octal.
|
||||||
// [0xB6DB_6DB6_DB6D_B6DC] B
|
// [0xB6DB_6DB6_DB6D_B6DC] B
|
||||||
{
|
(
|
||||||
let scalar_fixed_short = pallas::Scalar::from_u64(0xB6DB_6DB6_DB6D_B6DCu64);
|
"mul_with_double",
|
||||||
|
pallas::Base::from_u64(0xB6DB_6DB6_DB6D_B6DCu64),
|
||||||
|
pallas::Base::one(),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
let (result, _) = value_commit_v.mul(
|
for (name, magnitude, sign) in magnitude_signs.iter() {
|
||||||
layouter.namespace(|| "mul with double"),
|
let (result, _) = {
|
||||||
Some(scalar_fixed_short),
|
let magnitude_sign = load_magnitude_sign(
|
||||||
|
chip.clone(),
|
||||||
|
layouter.namespace(|| *name),
|
||||||
|
*magnitude,
|
||||||
|
*sign,
|
||||||
)?;
|
)?;
|
||||||
|
value_commit_v.mul(layouter.namespace(|| *name), magnitude_sign)?
|
||||||
|
};
|
||||||
|
// Move from base field into scalar field
|
||||||
|
let scalar = {
|
||||||
|
let magnitude = pallas::Scalar::from_bytes(&magnitude.to_bytes()).unwrap();
|
||||||
|
let sign = if *sign == pallas::Base::one() {
|
||||||
|
pallas::Scalar::one()
|
||||||
|
} else {
|
||||||
|
-pallas::Scalar::one()
|
||||||
|
};
|
||||||
|
magnitude * sign
|
||||||
|
};
|
||||||
constrain_equal(
|
constrain_equal(
|
||||||
chip,
|
chip.clone(),
|
||||||
layouter.namespace(|| "mul with double"),
|
layouter.namespace(|| *name),
|
||||||
base_val,
|
base_val,
|
||||||
scalar_fixed_short,
|
scalar,
|
||||||
result,
|
result,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue