Merge pull request #250 from zcash/update-halo2-assignedcell

Migrate to halo2 version with `AssignedCell`
This commit is contained in:
str4d 2021-12-09 15:30:40 +00:00 committed by GitHub
commit e4f338e758
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 902 additions and 1009 deletions

View File

@ -90,4 +90,4 @@ debug = true
[patch.crates-io]
zcash_note_encryption = { git = "https://github.com/zcash/librustzcash.git", rev = "35e75420657599fdc701cb45704878eb3fa2e59a" }
incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree.git", rev = "b7bd6246122a6e9ace8edb51553fbf5228906cbb" }
halo2 = { git = "https://github.com/zcash/halo2.git", rev = "8bfc58b7c76ae83ba5a9ed7ecdfe0ddfd40ed571" }
halo2 = { git = "https://github.com/zcash/halo2.git", rev = "afd7bc5469674cd08eae1634225fd02706a36a4f" }

View File

@ -12,10 +12,7 @@ use halo2::{
use pasta_curves::{pallas, vesta};
use orchard::{
circuit::gadget::{
poseidon::{Hash, Pow5Chip, Pow5Config},
utilities::{CellValue, Var},
},
circuit::gadget::poseidon::{Hash, Pow5Chip, Pow5Config},
primitives::poseidon::{self, ConstantLength, Spec},
};
use std::convert::TryInto;
@ -91,13 +88,12 @@ where
|mut region| {
let message_word = |i: usize| {
let value = self.message.map(|message_vals| message_vals[i]);
let cell = region.assign_advice(
region.assign_advice(
|| format!("load message_{}", i),
config.input[i],
0,
|| value.ok_or(Error::Synthesis),
)?;
Ok(CellValue::new(cell, value))
)
};
let message: Result<Vec<_>, Error> = (0..L).map(message_word).collect();
@ -121,7 +117,7 @@ where
0,
|| self.output.ok_or(Error::Synthesis),
)?;
region.constrain_equal(output.cell(), expected_var)
region.constrain_equal(output.cell(), expected_var.cell())
},
)
}

View File

@ -2,7 +2,7 @@
use group::{Curve, GroupEncoding};
use halo2::{
circuit::{floor_planner, Layouter},
circuit::{floor_planner, AssignedCell, Layouter},
plonk::{self, Advice, Column, Expression, Instance as InstanceColumn, Selector},
poly::Rotation,
transcript::{Blake2bRead, Blake2bWrite},
@ -50,7 +50,7 @@ use gadget::{
},
note_commit::NoteCommitConfig,
},
utilities::{copy, CellValue, UtilitiesInstructions, Var},
utilities::UtilitiesInstructions,
};
use std::convert::TryInto;
@ -117,7 +117,7 @@ pub struct Circuit {
}
impl UtilitiesInstructions<pallas::Base> for Circuit {
type Var = CellValue<pallas::Base>;
type Var = AssignedCell<pallas::Base, pallas::Base>;
}
impl plonk::Circuit<pallas::Base> for Circuit {
@ -405,7 +405,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
leaf_pos: self.pos,
path,
};
let leaf = *cm_old.extract_p().inner();
let leaf = cm_old.extract_p().inner().clone();
merkle_inputs.calculate_root(layouter.namespace(|| "MerkleCRH"), leaf)?
};
@ -454,7 +454,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
let (commitment, _) = {
let value_commit_v = ValueCommitV::get();
let value_commit_v = FixedPointShort::from_inner(ecc_chip.clone(), value_commit_v);
value_commit_v.mul(layouter.namespace(|| "[v_net] ValueCommitV"), v_net)?
value_commit_v.mul(layouter.namespace(|| "[v_net] ValueCommitV"), v_net.clone())?
};
// blind = [rcv] ValueCommitR
@ -481,7 +481,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
let nf_old = {
// hash_old = poseidon_hash(nk, rho_old)
let hash_old = {
let poseidon_message = [nk, rho_old];
let poseidon_message = [nk.clone(), rho_old.clone()];
let poseidon_hasher = PoseidonHash::<_, _, poseidon::P128Pow5T3, _, 3, 2>::init(
config.poseidon_chip(),
layouter.namespace(|| "Poseidon init"),
@ -501,32 +501,19 @@ impl plonk::Circuit<pallas::Base> for Circuit {
|mut region| {
config.q_add.enable(&mut region, 0)?;
copy(
&mut region,
|| "copy hash_old",
config.advices[7],
0,
&hash_old,
)?;
copy(
&mut region,
|| "copy psi_old",
config.advices[8],
0,
&psi_old,
)?;
hash_old.copy_advice(|| "copy hash_old", &mut region, config.advices[7], 0)?;
psi_old.copy_advice(|| "copy psi_old", &mut region, config.advices[8], 0)?;
let scalar_val = hash_old
.value()
.zip(psi_old.value())
.map(|(hash_old, psi_old)| hash_old + psi_old);
let cell = region.assign_advice(
region.assign_advice(
|| "poseidon_hash(nk, rho_old) + psi_old",
config.advices[6],
0,
|| scalar_val.ok_or(plonk::Error::Synthesis),
)?;
Ok(CellValue::new(cell, scalar_val))
)
},
)?;
@ -581,7 +568,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
config.sinsemilla_chip_1(),
ecc_chip.clone(),
layouter.namespace(|| "CommitIvk"),
*ak.extract_p().inner(),
ak.extract_p().inner().clone(),
nk,
rivk,
)?
@ -619,7 +606,7 @@ impl plonk::Circuit<pallas::Base> for Circuit {
config.ecc_chip(),
g_d_old.inner(),
pk_d_old.inner(),
v_old,
v_old.clone(),
rho_old,
psi_old,
rcm_old,
@ -675,8 +662,8 @@ impl plonk::Circuit<pallas::Base> for Circuit {
config.ecc_chip(),
g_d_new.inner(),
pk_d_new.inner(),
v_new,
*nf_old.inner(),
v_new.clone(),
nf_old.inner().clone(),
psi_new,
rcm_new,
)?;
@ -692,19 +679,13 @@ impl plonk::Circuit<pallas::Base> for Circuit {
layouter.assign_region(
|| "v_old - v_new = magnitude * sign",
|mut region| {
copy(&mut region, || "v_old", config.advices[0], 0, &v_old)?;
copy(&mut region, || "v_new", config.advices[1], 0, &v_new)?;
let (magnitude, sign) = v_net;
copy(
&mut region,
|| "v_net magnitude",
config.advices[2],
0,
&magnitude,
)?;
copy(&mut region, || "v_net sign", config.advices[3], 0, &sign)?;
v_old.copy_advice(|| "v_old", &mut region, config.advices[0], 0)?;
v_new.copy_advice(|| "v_new", &mut region, config.advices[1], 0)?;
let (magnitude, sign) = v_net.clone();
magnitude.copy_advice(|| "v_net magnitude", &mut region, config.advices[2], 0)?;
sign.copy_advice(|| "v_net sign", &mut region, config.advices[3], 0)?;
copy(&mut region, || "anchor", config.advices[4], 0, &anchor)?;
anchor.copy_advice(|| "anchor", &mut region, config.advices[4], 0)?;
region.assign_advice_from_instance(
|| "pub input anchor",
config.primary,

View File

@ -508,7 +508,9 @@ mod tests {
use super::chip::{EccChip, EccConfig};
use crate::circuit::gadget::utilities::lookup_range_check::LookupRangeCheckConfig;
struct MyCircuit {}
struct MyCircuit {
test_errors: bool,
}
#[allow(non_snake_case)]
impl Circuit<pallas::Base> for MyCircuit {
@ -516,7 +518,7 @@ mod tests {
type FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self {
MyCircuit {}
MyCircuit { test_errors: false }
}
fn configure(meta: &mut ConstraintSystem<pallas::Base>) -> Self::Config {
@ -634,6 +636,7 @@ mod tests {
q_val,
&q,
&p_neg,
self.test_errors,
)?;
}
@ -678,7 +681,7 @@ mod tests {
#[test]
fn ecc_chip() {
let k = 13;
let circuit = MyCircuit {};
let circuit = MyCircuit { test_errors: true };
let prover = MockProver::run(k, &circuit, vec![]).unwrap();
assert_eq!(prover.verify(), Ok(()))
}
@ -692,7 +695,7 @@ mod tests {
root.fill(&WHITE).unwrap();
let root = root.titled("Ecc Chip Layout", ("sans-serif", 60)).unwrap();
let circuit = MyCircuit {};
let circuit = MyCircuit { test_errors: false };
halo2::dev::CircuitLayout::default()
.render(13, &circuit, &root)
.unwrap();

View File

@ -1,16 +1,17 @@
use super::EccInstructions;
use crate::{
circuit::gadget::utilities::{
copy, lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions, Var,
lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions,
},
constants::{self, NullifierK, OrchardFixedBasesFull, ValueCommitV},
primitives::sinsemilla,
};
use arrayvec::ArrayVec;
use ff::Field;
use group::prime::PrimeCurveAffine;
use halo2::{
circuit::{Chip, Layouter},
circuit::{AssignedCell, Chip, Layouter},
plonk::{Advice, Column, ConstraintSystem, Error, Fixed},
};
use pasta_curves::{arithmetic::CurveAffine, pallas};
@ -26,12 +27,12 @@ pub(super) mod witness_point;
/// A curve point represented in affine (x, y) coordinates, or the
/// identity represented as (0, 0).
/// Each coordinate is assigned to a cell.
#[derive(Copy, Clone, Debug)]
#[derive(Clone, Debug)]
pub struct EccPoint {
/// x-coordinate
x: CellValue<pallas::Base>,
x: AssignedCell<pallas::Base, pallas::Base>,
/// y-coordinate
y: CellValue<pallas::Base>,
y: AssignedCell<pallas::Base, pallas::Base>,
}
impl EccPoint {
@ -40,8 +41,8 @@ impl EccPoint {
/// This is an internal API that we only use where we know we have a valid curve point
/// (specifically inside Sinsemilla).
pub(in crate::circuit::gadget) fn from_coordinates_unchecked(
x: CellValue<pallas::Base>,
y: CellValue<pallas::Base>,
x: AssignedCell<pallas::Base, pallas::Base>,
y: AssignedCell<pallas::Base, pallas::Base>,
) -> Self {
EccPoint { x, y }
}
@ -50,10 +51,10 @@ impl EccPoint {
pub fn point(&self) -> Option<pallas::Affine> {
match (self.x.value(), self.y.value()) {
(Some(x), Some(y)) => {
if x == pallas::Base::zero() && y == pallas::Base::zero() {
if x.is_zero_vartime() && y.is_zero_vartime() {
Some(pallas::Affine::identity())
} else {
Some(pallas::Affine::from_xy(x, y).unwrap())
Some(pallas::Affine::from_xy(*x, *y).unwrap())
}
}
_ => None,
@ -61,29 +62,29 @@ impl EccPoint {
}
/// The cell containing the affine short-Weierstrass x-coordinate,
/// or 0 for the zero point.
pub fn x(&self) -> CellValue<pallas::Base> {
self.x
pub fn x(&self) -> AssignedCell<pallas::Base, pallas::Base> {
self.x.clone()
}
/// The cell containing the affine short-Weierstrass y-coordinate,
/// or 0 for the zero point.
pub fn y(&self) -> CellValue<pallas::Base> {
self.y
pub fn y(&self) -> AssignedCell<pallas::Base, pallas::Base> {
self.y.clone()
}
#[cfg(test)]
fn is_identity(&self) -> Option<bool> {
self.x.value().map(|x| x == pallas::Base::zero())
self.x.value().map(|x| x.is_zero_vartime())
}
}
/// A non-identity point represented in affine (x, y) coordinates.
/// Each coordinate is assigned to a cell.
#[derive(Copy, Clone, Debug)]
#[derive(Clone, Debug)]
pub struct NonIdentityEccPoint {
/// x-coordinate
x: CellValue<pallas::Base>,
x: AssignedCell<pallas::Base, pallas::Base>,
/// y-coordinate
y: CellValue<pallas::Base>,
y: AssignedCell<pallas::Base, pallas::Base>,
}
impl NonIdentityEccPoint {
@ -92,8 +93,8 @@ impl NonIdentityEccPoint {
/// This is an internal API that we only use where we know we have a valid non-identity
/// curve point (specifically inside Sinsemilla).
pub(in crate::circuit::gadget) fn from_coordinates_unchecked(
x: CellValue<pallas::Base>,
y: CellValue<pallas::Base>,
x: AssignedCell<pallas::Base, pallas::Base>,
y: AssignedCell<pallas::Base, pallas::Base>,
) -> Self {
NonIdentityEccPoint { x, y }
}
@ -102,19 +103,19 @@ impl NonIdentityEccPoint {
pub fn point(&self) -> Option<pallas::Affine> {
match (self.x.value(), self.y.value()) {
(Some(x), Some(y)) => {
assert!(x != pallas::Base::zero() && y != pallas::Base::zero());
Some(pallas::Affine::from_xy(x, y).unwrap())
assert!(!x.is_zero_vartime() && !y.is_zero_vartime());
Some(pallas::Affine::from_xy(*x, *y).unwrap())
}
_ => None,
}
}
/// The cell containing the affine short-Weierstrass x-coordinate.
pub fn x(&self) -> CellValue<pallas::Base> {
self.x
pub fn x(&self) -> AssignedCell<pallas::Base, pallas::Base> {
self.x.clone()
}
/// The cell containing the affine short-Weierstrass y-coordinate.
pub fn y(&self) -> CellValue<pallas::Base> {
self.y
pub fn y(&self) -> AssignedCell<pallas::Base, pallas::Base> {
self.y.clone()
}
}
@ -177,7 +178,7 @@ impl Chip<pallas::Base> for EccChip {
}
impl UtilitiesInstructions<pallas::Base> for EccChip {
type Var = CellValue<pallas::Base>;
type Var = AssignedCell<pallas::Base, pallas::Base>;
}
impl EccChip {
@ -259,9 +260,15 @@ impl EccChip {
#[derive(Clone, Debug)]
pub struct EccScalarFixed {
value: Option<pallas::Scalar>,
windows: ArrayVec<CellValue<pallas::Base>, { constants::NUM_WINDOWS }>,
windows: ArrayVec<AssignedCell<pallas::Base, pallas::Base>, { constants::NUM_WINDOWS }>,
}
// TODO: Make V a `u64`
type MagnitudeCell = AssignedCell<pallas::Base, pallas::Base>;
// TODO: Make V an enum Sign { Positive, Negative }
type SignCell = AssignedCell<pallas::Base, pallas::Base>;
type MagnitudeSign = (MagnitudeCell, SignCell);
/// A signed short scalar used for fixed-base scalar multiplication.
/// A short scalar must have magnitude in the range [0..2^64), with
/// a sign of either 1 or -1.
@ -276,9 +283,10 @@ pub struct EccScalarFixed {
/// k_21 must be a single bit, i.e. 0 or 1.
#[derive(Clone, Debug)]
pub struct EccScalarFixedShort {
magnitude: CellValue<pallas::Base>,
sign: CellValue<pallas::Base>,
running_sum: ArrayVec<CellValue<pallas::Base>, { constants::NUM_WINDOWS_SHORT + 1 }>,
magnitude: MagnitudeCell,
sign: SignCell,
running_sum:
ArrayVec<AssignedCell<pallas::Base, pallas::Base>, { constants::NUM_WINDOWS_SHORT + 1 }>,
}
/// A base field element used for fixed-base scalar multiplication.
@ -292,23 +300,23 @@ pub struct EccScalarFixedShort {
/// `base_field_elem`.
#[derive(Clone, Debug)]
struct EccBaseFieldElemFixed {
base_field_elem: CellValue<pallas::Base>,
running_sum: ArrayVec<CellValue<pallas::Base>, { constants::NUM_WINDOWS + 1 }>,
base_field_elem: AssignedCell<pallas::Base, pallas::Base>,
running_sum: ArrayVec<AssignedCell<pallas::Base, pallas::Base>, { constants::NUM_WINDOWS + 1 }>,
}
impl EccBaseFieldElemFixed {
fn base_field_elem(&self) -> CellValue<pallas::Base> {
self.base_field_elem
fn base_field_elem(&self) -> AssignedCell<pallas::Base, pallas::Base> {
self.base_field_elem.clone()
}
}
impl EccInstructions<pallas::Affine> for EccChip {
type ScalarFixed = EccScalarFixed;
type ScalarFixedShort = EccScalarFixedShort;
type ScalarVar = CellValue<pallas::Base>;
type ScalarVar = AssignedCell<pallas::Base, pallas::Base>;
type Point = EccPoint;
type NonIdentityPoint = NonIdentityEccPoint;
type X = CellValue<pallas::Base>;
type X = AssignedCell<pallas::Base, pallas::Base>;
type FixedPoints = OrchardFixedBasesFull;
type FixedPointsBaseField = NullifierK;
type FixedPointsShort = ValueCommitV;
@ -396,7 +404,7 @@ impl EccInstructions<pallas::Affine> for EccChip {
let config = self.config().mul;
config.assign(
layouter.namespace(|| "variable-base scalar mul"),
*scalar,
scalar.clone(),
base,
)
}
@ -418,7 +426,7 @@ impl EccInstructions<pallas::Affine> for EccChip {
fn mul_fixed_short(
&self,
layouter: &mut impl Layouter<pallas::Base>,
magnitude_sign: (CellValue<pallas::Base>, CellValue<pallas::Base>),
magnitude_sign: MagnitudeSign,
base: &Self::FixedPointsShort,
) -> Result<(Self::Point, Self::ScalarFixedShort), Error> {
let config: mul_fixed::short::Config = self.config().mul_fixed_short;
@ -432,7 +440,7 @@ impl EccInstructions<pallas::Affine> for EccChip {
fn mul_fixed_base_field_elem(
&self,
layouter: &mut impl Layouter<pallas::Base>,
base_field_elem: CellValue<pallas::Base>,
base_field_elem: AssignedCell<pallas::Base, pallas::Base>,
base: &Self::FixedPointsBaseField,
) -> Result<Self::Point, Error> {
let config = self.config().mul_fixed_base_field;

View File

@ -1,6 +1,6 @@
use std::array;
use super::{copy, CellValue, EccPoint, Var};
use super::EccPoint;
use ff::{BatchInvert, Field};
use halo2::{
circuit::Region,
@ -223,12 +223,12 @@ impl Config {
self.q_add.enable(region, offset)?;
// Copy point `p` into `x_p`, `y_p` columns
copy(region, || "x_p", self.x_p, offset, &p.x)?;
copy(region, || "y_p", self.y_p, offset, &p.y)?;
p.x.copy_advice(|| "x_p", region, self.x_p, offset)?;
p.y.copy_advice(|| "y_p", region, self.y_p, offset)?;
// Copy point `q` into `x_qr`, `y_qr` columns
copy(region, || "x_q", self.x_qr, offset, &q.x)?;
copy(region, || "y_q", self.y_qr, offset, &q.y)?;
q.x.copy_advice(|| "x_q", region, self.x_qr, offset)?;
q.y.copy_advice(|| "y_q", region, self.y_qr, offset)?;
let (x_p, y_p) = (p.x.value(), p.y.value());
let (x_q, y_q) = (q.x.value(), q.y.value());
@ -248,7 +248,7 @@ impl Config {
let gamma = x_q;
let delta = y_q + y_p;
let mut inverses = [alpha, beta, gamma, delta];
let mut inverses = [alpha, *beta, *gamma, delta];
inverses.batch_invert();
inverses
});
@ -300,7 +300,7 @@ impl Config {
// know that x_q != x_p in this branch.
(y_q - y_p) * alpha
} else {
if y_p != pallas::Base::zero() {
if !y_p.is_zero_vartime() {
// 3(x_p)^2
let three_x_p_sq = pallas::Base::from_u64(3) * x_p.square();
// 1 / 2(y_p)
@ -327,13 +327,13 @@ impl Config {
.zip(lambda)
.map(|((((x_p, y_p), x_q), y_q), lambda)| {
{
if x_p == pallas::Base::zero() {
if x_p.is_zero_vartime() {
// 0 + Q = Q
(x_q, y_q)
} else if x_q == pallas::Base::zero() {
(*x_q, *y_q)
} else if x_q.is_zero_vartime() {
// P + 0 = P
(x_p, y_p)
} else if (x_q == x_p) && (y_q == -y_p) {
(*x_p, *y_p)
} else if (x_q == x_p) && (*y_q == -y_p) {
// P + (-P) maps to (0,0)
(pallas::Base::zero(), pallas::Base::zero())
} else {
@ -365,8 +365,8 @@ impl Config {
)?;
let result = EccPoint {
x: CellValue::<pallas::Base>::new(x_r_cell, x_r),
y: CellValue::<pallas::Base>::new(y_r_cell, y_r),
x: x_r_cell,
y: y_r_cell,
};
#[cfg(test)]
@ -414,7 +414,9 @@ pub mod tests {
// Check complete addition P + (-P)
let zero = {
let result = p.add(layouter.namespace(|| "P + (-P)"), p_neg)?;
assert!(result.inner().is_identity().unwrap());
if let Some(is_identity) = result.inner().is_identity() {
assert!(is_identity);
}
result
};

View File

@ -1,6 +1,7 @@
use std::{array, collections::HashSet};
use super::{copy, CellValue, NonIdentityEccPoint, Var};
use super::NonIdentityEccPoint;
use ff::Field;
use group::Curve;
use halo2::{
circuit::Region,
@ -96,9 +97,9 @@ impl Config {
.zip(y_q)
.map(|(((x_p, y_p), x_q), y_q)| {
// P is point at infinity
if (x_p == pallas::Base::zero() && y_p == pallas::Base::zero())
if (x_p.is_zero_vartime() && y_p.is_zero_vartime())
// Q is point at infinity
|| (x_q == pallas::Base::zero() && y_q == pallas::Base::zero())
|| (x_q.is_zero_vartime() && y_q.is_zero_vartime())
// x_p = x_q
|| (x_p == x_q)
{
@ -110,12 +111,12 @@ impl Config {
.transpose()?;
// Copy point `p` into `x_p`, `y_p` columns
copy(region, || "x_p", self.x_p, offset, &p.x)?;
copy(region, || "y_p", self.y_p, offset, &p.y)?;
p.x.copy_advice(|| "x_p", region, self.x_p, offset)?;
p.y.copy_advice(|| "y_p", region, self.y_p, offset)?;
// Copy point `q` into `x_qr`, `y_qr` columns
copy(region, || "x_q", self.x_qr, offset, &q.x)?;
copy(region, || "y_q", self.y_qr, offset, &q.y)?;
q.x.copy_advice(|| "x_q", region, self.x_qr, offset)?;
q.y.copy_advice(|| "y_q", region, self.y_qr, offset)?;
// Compute the sum `P + Q = R`
let r = {
@ -148,8 +149,8 @@ impl Config {
)?;
let result = NonIdentityEccPoint {
x: CellValue::<pallas::Base>::new(x_r_var, x_r),
y: CellValue::<pallas::Base>::new(y_r_var, y_r),
x: x_r_var,
y: y_r_var,
};
Ok(result)
@ -175,6 +176,7 @@ pub mod tests {
q_val: pallas::Affine,
q: &NonIdentityPoint<pallas::Affine, EccChip>,
p_neg: &NonIdentityPoint<pallas::Affine, EccChip>,
test_errors: bool,
) -> Result<(), Error> {
// P + Q
{
@ -187,13 +189,15 @@ pub mod tests {
result.constrain_equal(layouter.namespace(|| "constrain P + Q"), &witnessed_result)?;
}
// P + P should return an error
p.add_incomplete(layouter.namespace(|| "P + P"), p)
.expect_err("P + P should return an error");
if test_errors {
// P + P should return an error
p.add_incomplete(layouter.namespace(|| "P + P"), p)
.expect_err("P + P should return an error");
// P + (-P) should return an error
p.add_incomplete(layouter.namespace(|| "P + (-P)"), p_neg)
.expect_err("P + (-P) should return an error");
// P + (-P) should return an error
p.add_incomplete(layouter.namespace(|| "P + (-P)"), p_neg)
.expect_err("P + (-P) should return an error");
}
Ok(())
}

View File

@ -1,8 +1,6 @@
use super::{add, CellValue, EccPoint, NonIdentityEccPoint, Var};
use super::{add, EccPoint, NonIdentityEccPoint};
use crate::{
circuit::gadget::utilities::{
bool_check, copy, lookup_range_check::LookupRangeCheckConfig, ternary,
},
circuit::gadget::utilities::{bool_check, lookup_range_check::LookupRangeCheckConfig, ternary},
constants::T_Q,
primitives::sinsemilla,
};
@ -15,7 +13,7 @@ use bigint::U256;
use ff::PrimeField;
use halo2::{
arithmetic::FieldExt,
circuit::{Layouter, Region},
circuit::{AssignedCell, Layouter, Region},
plonk::{Advice, Column, ConstraintSystem, Error, Selector},
poly::Rotation,
};
@ -166,16 +164,16 @@ impl Config {
pub(super) fn assign(
&self,
mut layouter: impl Layouter<pallas::Base>,
alpha: CellValue<pallas::Base>,
alpha: AssignedCell<pallas::Base, pallas::Base>,
base: &NonIdentityEccPoint,
) -> Result<(EccPoint, CellValue<pallas::Base>), Error> {
) -> Result<(EccPoint, AssignedCell<pallas::Base, pallas::Base>), Error> {
let (result, zs): (EccPoint, Vec<Z<pallas::Base>>) = layouter.assign_region(
|| "variable-base scalar mul",
|mut region| {
let offset = 0;
// Case `base` into an `EccPoint` for later use.
let base_point: EccPoint = (*base).into();
let base_point: EccPoint = base.clone().into();
// Decompose `k = alpha + t_q` bitwise (big-endian bit order).
let bits = decompose_for_scalar_mul(alpha.value());
@ -194,16 +192,12 @@ impl Config {
let offset = offset + 1;
// Initialize the running sum for scalar decomposition to zero
let z_init = {
let z_init_cell = region.assign_advice_from_constant(
|| "z_init = 0",
self.hi_config.z,
offset,
pallas::Base::zero(),
)?;
Z(CellValue::new(z_init_cell, Some(pallas::Base::zero())))
};
let z_init = Z(region.assign_advice_from_constant(
|| "z_init = 0",
self.hi_config.z,
offset,
pallas::Base::zero(),
)?);
// Double-and-add (incomplete addition) for the `hi` half of the scalar decomposition
let (x_a, y_a, zs_incomplete_hi) = self.hi_config.double_and_add(
@ -211,7 +205,7 @@ impl Config {
offset,
base,
bits_incomplete_hi,
(X(acc.x), Y(acc.y), z_init),
(X(acc.x), Y(acc.y), z_init.clone()),
)?;
// Double-and-add (incomplete addition) for the `lo` half of the scalar decomposition
@ -221,7 +215,7 @@ impl Config {
offset,
base,
bits_incomplete_lo,
(x_a, y_a, *z),
(x_a, y_a, z.clone()),
)?;
// Move from incomplete addition to complete addition.
@ -245,7 +239,7 @@ impl Config {
&base_point,
x_a,
y_a,
*z,
z.clone(),
)?
};
@ -253,8 +247,8 @@ impl Config {
let offset = offset + COMPLETE_RANGE.len() * 2;
// Process the least significant bit
let z_1 = zs_complete.last().unwrap();
let (result, z_0) = self.process_lsb(&mut region, offset, base, acc, *z_1, lsb)?;
let z_1 = zs_complete.last().unwrap().clone();
let (result, z_0) = self.process_lsb(&mut region, offset, base, acc, z_1, lsb)?;
#[cfg(test)]
// Check that the correct multiple is obtained.
@ -292,8 +286,11 @@ impl Config {
},
)?;
self.overflow_config
.overflow_check(layouter.namespace(|| "overflow check"), alpha, &zs)?;
self.overflow_config.overflow_check(
layouter.namespace(|| "overflow check"),
alpha.clone(),
&zs,
)?;
Ok((result, alpha))
}
@ -339,29 +336,19 @@ impl Config {
|| z_0_val.ok_or(Error::Synthesis),
)?;
Z(CellValue::new(z_0_cell, z_0_val))
Z(z_0_cell)
};
// Copy in `base_x`, `base_y` to use in the LSB gate
copy(
region,
|| "copy base_x",
self.add_config.x_p,
offset + 1,
&base.x(),
)?;
copy(
region,
|| "copy base_y",
self.add_config.y_p,
offset + 1,
&base.y(),
)?;
base.x()
.copy_advice(|| "copy base_x", region, self.add_config.x_p, offset + 1)?;
base.y()
.copy_advice(|| "copy base_y", region, self.add_config.y_p, offset + 1)?;
// If `lsb` is 0, return `Acc + (-P)`. If `lsb` is 1, simply return `Acc + 0`.
let x = if let Some(lsb) = lsb {
if !lsb {
base.x.value()
base.x.value().cloned()
} else {
Some(pallas::Base::zero())
}
@ -394,8 +381,8 @@ impl Config {
)?;
let p = EccPoint {
x: CellValue::<pallas::Base>::new(x_cell, x),
y: CellValue::<pallas::Base>::new(y_cell, y),
x: x_cell,
y: y_cell,
};
// Return the result of the final complete addition as `[scalar]B`
@ -407,38 +394,38 @@ impl Config {
#[derive(Clone, Debug)]
// `x`-coordinate of the accumulator.
struct X<F: FieldExt>(CellValue<F>);
struct X<F: FieldExt>(AssignedCell<F, F>);
impl<F: FieldExt> Deref for X<F> {
type Target = CellValue<F>;
type Target = AssignedCell<F, F>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Copy, Clone, Debug)]
#[derive(Clone, Debug)]
// `y`-coordinate of the accumulator.
struct Y<F: FieldExt>(CellValue<F>);
struct Y<F: FieldExt>(AssignedCell<F, F>);
impl<F: FieldExt> Deref for Y<F> {
type Target = CellValue<F>;
type Target = AssignedCell<F, F>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Copy, Clone, Debug)]
#[derive(Clone, Debug)]
// Cumulative sum `z` used to decompose the scalar.
struct Z<F: FieldExt>(CellValue<F>);
struct Z<F: FieldExt>(AssignedCell<F, F>);
impl<F: FieldExt> Deref for Z<F> {
type Target = CellValue<F>;
type Target = AssignedCell<F, F>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
fn decompose_for_scalar_mul(scalar: Option<pallas::Base>) -> Vec<Option<bool>> {
fn decompose_for_scalar_mul(scalar: Option<&pallas::Base>) -> Vec<Option<bool>> {
let bitstring = scalar.map(|scalar| {
// We use `k = scalar + t_q` in the double-and-add algorithm, where
// the scalar field `F_q = 2^254 + t_q`.
@ -548,7 +535,9 @@ pub mod tests {
chip.load_private(layouter.namespace(|| "zero"), column, Some(scalar_val))?;
p.mul(layouter.namespace(|| "[0]B"), &scalar)?
};
assert!(result.inner().is_identity().unwrap());
if let Some(is_identity) = result.inner().is_identity() {
assert!(is_identity);
}
}
// [-1]B (the largest possible base field element)

View File

@ -1,4 +1,4 @@
use super::super::{add, copy, CellValue, EccPoint, Var};
use super::super::{add, EccPoint};
use super::{COMPLETE_RANGE, X, Y, Z};
use crate::circuit::gadget::utilities::{bool_check, ternary};
@ -106,16 +106,15 @@ impl Config {
}
// Use x_a, y_a output from incomplete addition
let mut acc = EccPoint { x: *x_a, y: *y_a };
let mut acc = EccPoint { x: x_a.0, y: y_a.0 };
// Copy running sum `z` from incomplete addition
let mut z = {
let z = copy(
region,
let z = z.copy_advice(
|| "Copy `z` running sum from incomplete addition",
region,
self.z_complete,
offset,
&z,
)?;
Z(z)
};
@ -146,37 +145,40 @@ impl Config {
row + offset + 2,
|| z_val.ok_or(Error::Synthesis),
)?;
Z(CellValue::new(z_cell, z_val))
Z(z_cell)
};
zs.push(z);
zs.push(z.clone());
// Assign `y_p` for complete addition.
let y_p = {
let base_y = copy(
region,
let base_y = base.y.copy_advice(
|| "Copy `base.y`",
region,
self.z_complete,
row + offset + 1,
&base.y,
)?;
// If the bit is set, use `y`; if the bit is not set, use `-y`
let y_p = base_y
.value()
.zip(k.as_ref())
.map(|(base_y, k)| if !k { -base_y } else { base_y });
let y_p =
base_y
.value()
.cloned()
.zip(k.as_ref())
.map(|(base_y, k)| if !k { -base_y } else { base_y });
let y_p_cell = region.assign_advice(
region.assign_advice(
|| "y_p",
self.add_config.y_p,
row + offset,
|| y_p.ok_or(Error::Synthesis),
)?;
CellValue::<pallas::Base>::new(y_p_cell, y_p)
)?
};
// U = P if the bit is set; U = -P is the bit is not set.
let U = EccPoint { x: base.x, y: y_p };
let U = EccPoint {
x: base.x.clone(),
y: y_p,
};
// Acc + U
let tmp_acc = self

View File

@ -1,4 +1,4 @@
use super::super::{copy, CellValue, NonIdentityEccPoint, Var};
use super::super::NonIdentityEccPoint;
use super::{X, Y, Z};
use crate::circuit::gadget::utilities::bool_check;
use ff::Field;
@ -190,14 +190,14 @@ impl<const NUM_BITS: usize> Config<NUM_BITS> {
assert_eq!(bits.len(), NUM_BITS);
// Handle exceptional cases
let (x_p, y_p) = (base.x.value(), base.y.value());
let (x_a, y_a) = (acc.0.value(), acc.1.value());
let (x_p, y_p) = (base.x.value().cloned(), base.y.value().cloned());
let (x_a, y_a) = (acc.0.value().cloned(), acc.1.value().cloned());
if let (Some(x_a), Some(y_a), Some(x_p), Some(y_p)) = (x_a, y_a, x_p, y_p) {
// A is point at infinity
if (x_p == pallas::Base::zero() && y_p == pallas::Base::zero())
if (x_p.is_zero_vartime() && y_p.is_zero_vartime())
// Q is point at infinity
|| (x_a == pallas::Base::zero() && y_a == pallas::Base::zero())
|| (x_a.is_zero_vartime() && y_a.is_zero_vartime())
// x_p = x_a
|| (x_p == x_a)
{
@ -223,13 +223,17 @@ impl<const NUM_BITS: usize> Config<NUM_BITS> {
// Initialise double-and-add
let (mut x_a, mut y_a, mut z) = {
// Initialise the running `z` sum for the scalar bits.
let z = copy(region, || "starting z", self.z, offset, &acc.2)?;
let z = acc.2.copy_advice(|| "starting z", region, self.z, offset)?;
// Initialise acc
let x_a = copy(region, || "starting x_a", self.x_a, offset + 1, &acc.0)?;
let y_a = copy(region, || "starting y_a", self.lambda1, offset, &acc.1)?;
let x_a = acc
.0
.copy_advice(|| "starting x_a", region, self.x_a, offset + 1)?;
let y_a = acc
.1
.copy_advice(|| "starting y_a", region, self.lambda1, offset)?;
(x_a, y_a.value(), z)
(x_a, y_a.value().cloned(), z)
};
// Increase offset by 1; we used row 0 for initializing `z`.
@ -244,14 +248,13 @@ impl<const NUM_BITS: usize> Config<NUM_BITS> {
let z_val = z.value().zip(k.as_ref()).map(|(z_val, k)| {
pallas::Base::from_u64(2) * z_val + pallas::Base::from_u64(*k as u64)
});
let z_cell = region.assign_advice(
z = region.assign_advice(
|| "z",
self.z,
row + offset,
|| z_val.ok_or(Error::Synthesis),
)?;
z = CellValue::new(z_cell, z_val);
zs.push(Z(z));
zs.push(Z(z.clone()));
// Assign `x_p`, `y_p`
region.assign_advice(
@ -313,30 +316,26 @@ impl<const NUM_BITS: usize> Config<NUM_BITS> {
.zip(x_r)
.map(|((lambda2, x_a), x_r)| lambda2.square() - x_a - x_r);
y_a = lambda2
.zip(x_a.value())
.zip(x_a.value().cloned())
.zip(x_a_new)
.zip(y_a)
.map(|(((lambda2, x_a), x_a_new), y_a)| lambda2 * (x_a - x_a_new) - y_a);
let x_a_val = x_a_new;
let x_a_cell = region.assign_advice(
x_a = region.assign_advice(
|| "x_a",
self.x_a,
row + offset + 1,
|| x_a_val.ok_or(Error::Synthesis),
)?;
x_a = CellValue::new(x_a_cell, x_a_val);
}
// Witness final y_a
let y_a = {
let cell = region.assign_advice(
|| "y_a",
self.lambda1,
offset + NUM_BITS,
|| y_a.ok_or(Error::Synthesis),
)?;
CellValue::new(cell, y_a)
};
let y_a = region.assign_advice(
|| "y_a",
self.lambda1,
offset + NUM_BITS,
|| y_a.ok_or(Error::Synthesis),
)?;
Ok((X(x_a), Y(y_a), zs))
}

View File

@ -1,9 +1,9 @@
use super::super::{copy, CellValue, Var};
use super::Z;
use crate::{
circuit::gadget::utilities::lookup_range_check::LookupRangeCheckConfig, constants::T_Q,
primitives::sinsemilla,
};
use halo2::circuit::AssignedCell;
use halo2::{
circuit::Layouter,
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector},
@ -99,7 +99,7 @@ impl Config {
pub(super) fn overflow_check(
&self,
mut layouter: impl Layouter<pallas::Base>,
alpha: CellValue<pallas::Base>,
alpha: AssignedCell<pallas::Base, 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
@ -107,7 +107,7 @@ impl Config {
// In the overflow check gate, we check that s is properly derived
// from alpha and k_254.
let s = {
let k_254 = *zs[254];
let k_254 = zs[254].clone();
let s_val = alpha
.value()
.zip(k_254.value())
@ -116,13 +116,12 @@ impl Config {
layouter.assign_region(
|| "s = alpha + k_254 ⋅ 2^130",
|mut region| {
let s_cell = region.assign_advice(
region.assign_advice(
|| "s = alpha + k_254 ⋅ 2^130",
self.advices[0],
0,
|| s_val.ok_or(Error::Synthesis),
)?;
Ok(CellValue::new(s_cell, s_val))
)
},
)?
};
@ -130,7 +129,7 @@ impl Config {
// Subtract the first 130 low bits of s = alpha + k_254 ⋅ 2^130
// using thirteen 10-bit lookups, s_{0..=129}
let s_minus_lo_130 =
self.s_minus_lo_130(layouter.namespace(|| "decompose s_{0..=129}"), s)?;
self.s_minus_lo_130(layouter.namespace(|| "decompose s_{0..=129}"), s.clone())?;
layouter.assign_region(
|| "overflow check",
@ -141,21 +140,15 @@ impl Config {
self.q_mul_overflow.enable(&mut region, offset + 1)?;
// Copy `z_0`
copy(&mut region, || "copy z_0", self.advices[0], offset, &*zs[0])?;
zs[0].copy_advice(|| "copy z_0", &mut region, self.advices[0], offset)?;
// Copy `z_130`
copy(
&mut region,
|| "copy z_130",
self.advices[0],
offset + 1,
&*zs[130],
)?;
zs[130].copy_advice(|| "copy z_130", &mut region, self.advices[0], offset + 1)?;
// Witness η = inv0(z_130), where inv0(x) = 0 if x = 0, 1/x otherwise
{
let eta = zs[130].value().map(|z_130| {
if z_130 == pallas::Base::zero() {
if z_130.is_zero_vartime() {
pallas::Base::zero()
} else {
z_130.invert().unwrap()
@ -170,34 +163,26 @@ impl Config {
}
// Copy `k_254` = z_254
copy(
&mut region,
|| "copy k_254",
self.advices[1],
offset,
&*zs[254],
)?;
zs[254].copy_advice(|| "copy k_254", &mut region, self.advices[1], offset)?;
// Copy original alpha
copy(
&mut region,
alpha.copy_advice(
|| "copy original alpha",
&mut region,
self.advices[1],
offset + 1,
&alpha,
)?;
// Copy weighted sum of the decomposition of s = alpha + k_254 ⋅ 2^130.
copy(
&mut region,
s_minus_lo_130.copy_advice(
|| "copy s_minus_lo_130",
&mut region,
self.advices[1],
offset + 2,
&s_minus_lo_130,
)?;
// Copy witnessed s to check that it was properly derived from alpha and k_254.
copy(&mut region, || "copy s", self.advices[2], offset + 1, &s)?;
s.copy_advice(|| "copy s", &mut region, self.advices[2], offset + 1)?;
Ok(())
},
@ -209,8 +194,8 @@ impl Config {
fn s_minus_lo_130(
&self,
mut layouter: impl Layouter<pallas::Base>,
s: CellValue<pallas::Base>,
) -> Result<CellValue<pallas::Base>, Error> {
s: AssignedCell<pallas::Base, pallas::Base>,
) -> Result<AssignedCell<pallas::Base, pallas::Base>, Error> {
// Number of k-bit words we can use in the lookup decomposition.
let num_words = 130 / sinsemilla::K;
assert!(num_words * sinsemilla::K == 130);
@ -223,6 +208,6 @@ impl Config {
false,
)?;
// (s - (2^0 s_0 + 2^1 s_1 + ... + 2^129 s_129)) / 2^130
Ok(zs[zs.len() - 1])
Ok(zs[zs.len() - 1].clone())
}
}

View File

@ -1,6 +1,6 @@
use super::{
add, add_incomplete, CellValue, EccBaseFieldElemFixed, EccScalarFixed, EccScalarFixedShort,
NonIdentityEccPoint, Var,
add, add_incomplete, EccBaseFieldElemFixed, EccScalarFixed, EccScalarFixedShort,
NonIdentityEccPoint,
};
use crate::circuit::gadget::utilities::decompose_running_sum::RunningSumConfig;
use crate::constants::{
@ -10,7 +10,7 @@ use crate::constants::{
use group::Curve;
use halo2::{
circuit::Region,
circuit::{AssignedCell, Region},
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, Selector, VirtualCells},
poly::Rotation,
};
@ -353,26 +353,24 @@ impl Config {
assert!(x != pallas::Base::zero());
x
});
let x_cell = region.assign_advice(
let x = region.assign_advice(
|| format!("mul_b_x, window {}", w),
self.x_p,
offset + w,
|| x.ok_or(Error::Synthesis),
)?;
let x = CellValue::new(x_cell, x);
let y = mul_b.map(|mul_b| {
let y = *mul_b.y();
assert!(y != pallas::Base::zero());
y
});
let y_cell = region.assign_advice(
let y = region.assign_advice(
|| format!("mul_b_y, window {}", w),
self.y_p,
offset + w,
|| y.ok_or(Error::Synthesis),
)?;
let y = CellValue::new(y_cell, y);
NonIdentityEccPoint { x, y }
};
@ -471,27 +469,24 @@ impl Config {
assert!(x != pallas::Base::zero());
x
});
let x_cell = region.assign_advice(
let x = region.assign_advice(
|| format!("mul_b_x, window {}", NUM_WINDOWS - 1),
self.x_p,
offset + NUM_WINDOWS - 1,
|| x.ok_or(Error::Synthesis),
)?;
let x = CellValue::new(x_cell, x);
let y = mul_b.map(|mul_b| {
let y = *mul_b.y();
assert!(y != pallas::Base::zero());
y
});
let y_cell = region.assign_advice(
let y = region.assign_advice(
|| format!("mul_b_y, window {}", NUM_WINDOWS - 1),
self.y_p,
offset + NUM_WINDOWS - 1,
|| y.ok_or(Error::Synthesis),
)?;
let y = CellValue::new(y_cell, y);
NonIdentityEccPoint { x, y }
};
@ -528,7 +523,7 @@ impl ScalarFixed {
// The scalar decomposition was done in the base field. For computation
// outside the circuit, we now convert them back into the scalar field.
fn windows_field(&self) -> Vec<Option<pallas::Scalar>> {
let running_sum_to_windows = |zs: Vec<CellValue<pallas::Base>>| {
let running_sum_to_windows = |zs: Vec<AssignedCell<pallas::Base, pallas::Base>>| {
(0..(zs.len() - 1))
.map(|idx| {
let z_cur = zs[idx].value();

View File

@ -3,12 +3,12 @@ use super::H_BASE;
use crate::{
circuit::gadget::utilities::{
bitrange_subset, copy, lookup_range_check::LookupRangeCheckConfig, range_check, CellValue,
Var,
bitrange_subset, lookup_range_check::LookupRangeCheckConfig, range_check,
},
constants::{self, T_P},
primitives::sinsemilla,
};
use halo2::circuit::AssignedCell;
use halo2::{
circuit::Layouter,
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector},
@ -160,7 +160,7 @@ impl Config {
pub fn assign(
&self,
mut layouter: impl Layouter<pallas::Base>,
scalar: CellValue<pallas::Base>,
scalar: AssignedCell<pallas::Base, pallas::Base>,
base: NullifierK,
) -> Result<EccPoint, Error> {
let (scalar, acc, mul_b) = layouter.assign_region(
@ -173,13 +173,13 @@ impl Config {
let running_sum = self.super_config.running_sum_config.copy_decompose(
&mut region,
offset,
scalar,
scalar.clone(),
true,
constants::L_ORCHARD_BASE,
constants::NUM_WINDOWS,
)?;
EccBaseFieldElemFixed {
base_field_elem: running_sum[0],
base_field_elem: running_sum[0].clone(),
running_sum: (*running_sum).as_slice().try_into().unwrap(),
}
};
@ -203,8 +203,8 @@ impl Config {
|| "Base-field elem fixed-base mul (complete addition)",
|mut region| {
self.super_config.add_config.assign_region(
&mul_b.into(),
&acc.into(),
&mul_b.clone().into(),
&acc.clone().into(),
0,
&mut region,
)
@ -249,9 +249,9 @@ impl Config {
// => z_13_alpha_0_prime = 0
//
let (alpha, running_sum) = (scalar.base_field_elem, &scalar.running_sum);
let z_43_alpha = running_sum[43];
let z_44_alpha = running_sum[44];
let z_84_alpha = running_sum[84];
let z_43_alpha = running_sum[43].clone();
let z_44_alpha = running_sum[44].clone();
let z_84_alpha = running_sum[84].clone();
// α_0 = α - z_84_alpha * 2^252
let alpha_0 = alpha
@ -275,9 +275,9 @@ impl Config {
13,
false,
)?;
let alpha_0_prime = zs[0];
let alpha_0_prime = zs[0].clone();
(alpha_0_prime, zs[13])
(alpha_0_prime, zs[13].clone())
};
layouter.assign_region(
@ -291,21 +291,14 @@ impl Config {
let offset = 0;
// Copy α
copy(
&mut region,
|| "Copy α",
self.canon_advices[0],
offset,
&alpha,
)?;
alpha.copy_advice(|| "Copy α", &mut region, self.canon_advices[0], offset)?;
// z_84_alpha = the top three bits of alpha.
copy(
&mut region,
z_84_alpha.copy_advice(
|| "Copy z_84_alpha",
&mut region,
self.canon_advices[2],
offset,
&z_84_alpha,
)?;
}
@ -314,12 +307,11 @@ impl Config {
let offset = 1;
// Copy alpha_0_prime = alpha_0 + 2^130 - t_p.
// We constrain this in the custom gate to be derived correctly.
copy(
&mut region,
alpha_0_prime.copy_advice(
|| "Copy α_0 + 2^130 - t_p",
&mut region,
self.canon_advices[0],
offset,
&alpha_0_prime,
)?;
// Decompose α into three pieces,
@ -348,30 +340,27 @@ impl Config {
{
let offset = 2;
// Copy z_13_alpha_0_prime
copy(
&mut region,
z_13_alpha_0_prime.copy_advice(
|| "Copy z_13_alpha_0_prime",
&mut region,
self.canon_advices[0],
offset,
&z_13_alpha_0_prime,
)?;
// Copy z_44_alpha
copy(
&mut region,
z_44_alpha.copy_advice(
|| "Copy z_44_alpha",
&mut region,
self.canon_advices[1],
offset,
&z_44_alpha,
)?;
// Copy z_43_alpha
copy(
&mut region,
z_43_alpha.copy_advice(
|| "Copy z_43_alpha",
&mut region,
self.canon_advices[2],
offset,
&z_43_alpha,
)?;
}
@ -498,7 +487,9 @@ pub mod tests {
chip.load_private(layouter.namespace(|| "zero"), column, Some(scalar_fixed))?;
base.mul(layouter.namespace(|| "mul by zero"), scalar_fixed)?
};
assert!(result.inner().is_identity().unwrap());
if let Some(is_identity) = result.inner().is_identity() {
assert!(is_identity);
}
}
// [-1]B is the largest base field element

View File

@ -1,12 +1,12 @@
use super::super::{EccPoint, EccScalarFixed, OrchardFixedBasesFull};
use crate::{
circuit::gadget::utilities::{range_check, CellValue, Var},
circuit::gadget::utilities::range_check,
constants::{self, util, L_ORCHARD_SCALAR, NUM_WINDOWS},
};
use arrayvec::ArrayVec;
use halo2::{
circuit::{Layouter, Region},
circuit::{AssignedCell, Layouter, Region},
plonk::{ConstraintSystem, Error, Selector},
poly::Rotation,
};
@ -76,7 +76,7 @@ impl Config {
scalar: Option<pallas::Scalar>,
offset: usize,
region: &mut Region<'_, pallas::Base>,
) -> Result<ArrayVec<CellValue<pallas::Base>, NUM_WINDOWS>, Error> {
) -> Result<ArrayVec<AssignedCell<pallas::Base, pallas::Base>, NUM_WINDOWS>, Error> {
// Enable `q_mul_fixed_full` selector
for idx in 0..NUM_WINDOWS {
self.q_mul_fixed_full.enable(region, offset + idx)?;
@ -85,14 +85,15 @@ impl Config {
// Decompose scalar into `k-bit` windows
let scalar_windows: Option<Vec<u8>> = scalar.map(|scalar| {
util::decompose_word::<pallas::Scalar>(
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 mut windows: ArrayVec<AssignedCell<pallas::Base, 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);
@ -111,7 +112,7 @@ impl Config {
offset + idx,
|| window.ok_or(Error::Synthesis),
)?;
windows.push(CellValue::new(window_cell, window));
windows.push(window_cell);
}
Ok(windows)
@ -149,8 +150,8 @@ impl Config {
|| "Full-width fixed-base mul (last window, complete addition)",
|mut region| {
self.super_config.add_config.assign_region(
&mul_b.into(),
&acc.into(),
&mul_b.clone().into(),
&acc.clone().into(),
0,
&mut region,
)
@ -294,7 +295,9 @@ pub mod tests {
{
let scalar_fixed = pallas::Scalar::zero();
let (result, _) = base.mul(layouter.namespace(|| "mul by zero"), Some(scalar_fixed))?;
assert!(result.inner().is_identity().unwrap());
if let Some(is_identity) = result.inner().is_identity() {
assert!(is_identity);
}
}
// [-1]B is the largest scalar field element.

View File

@ -2,7 +2,7 @@ use std::{array, convert::TryInto};
use super::super::{EccPoint, EccScalarFixedShort};
use crate::{
circuit::gadget::utilities::{bool_check, copy, CellValue, Var},
circuit::gadget::{ecc::chip::MagnitudeSign, utilities::bool_check},
constants::{ValueCommitV, L_VALUE, NUM_WINDOWS_SHORT},
};
@ -74,7 +74,7 @@ impl Config {
&self,
region: &mut Region<'_, pallas::Base>,
offset: usize,
magnitude_sign: (CellValue<pallas::Base>, CellValue<pallas::Base>),
magnitude_sign: MagnitudeSign,
) -> Result<EccScalarFixedShort, Error> {
let (magnitude, sign) = magnitude_sign;
@ -82,7 +82,7 @@ impl Config {
let running_sum = self.super_config.running_sum_config.copy_decompose(
region,
offset,
magnitude,
magnitude.clone(),
true,
L_VALUE,
NUM_WINDOWS_SHORT,
@ -98,7 +98,7 @@ impl Config {
pub fn assign(
&self,
mut layouter: impl Layouter<pallas::Base>,
magnitude_sign: (CellValue<pallas::Base>, CellValue<pallas::Base>),
magnitude_sign: MagnitudeSign,
base: &ValueCommitV,
) -> Result<(EccPoint, EccScalarFixedShort), Error> {
let (scalar, acc, mul_b) = layouter.assign_region(
@ -107,7 +107,7 @@ impl Config {
let offset = 0;
// Decompose the scalar
let scalar = self.decompose(&mut region, offset, magnitude_sign)?;
let scalar = self.decompose(&mut region, offset, magnitude_sign.clone())?;
let (acc, mul_b) = self.super_config.assign_region_inner::<NUM_WINDOWS_SHORT>(
&mut region,
@ -128,8 +128,8 @@ impl Config {
let offset = 0;
// Add to the cumulative sum to get `[magnitude]B`.
let magnitude_mul = self.super_config.add_config.assign_region(
&mul_b.into(),
&acc.into(),
&mul_b.clone().into(),
&acc.clone().into(),
offset,
&mut region,
)?;
@ -138,32 +138,25 @@ impl Config {
let offset = offset + 1;
// Copy sign to `window` column
let sign = copy(
&mut region,
let sign = scalar.sign.copy_advice(
|| "sign",
&mut region,
self.super_config.window,
offset,
&scalar.sign,
)?;
// Copy last window to `u` column.
// (Although the last window is not a `u` value; we are copying it into the `u`
// column because there is an available cell there.)
let z_21 = scalar.running_sum[21];
copy(
&mut region,
|| "last_window",
self.super_config.u,
offset,
&z_21,
)?;
let z_21 = scalar.running_sum[21].clone();
z_21.copy_advice(|| "last_window", &mut region, self.super_config.u, offset)?;
// Conditionally negate `y`-coordinate
let y_val = if let Some(sign) = sign.value() {
if sign == -pallas::Base::one() {
magnitude_mul.y.value().map(|y: pallas::Base| -y)
if sign == &-pallas::Base::one() {
magnitude_mul.y.value().cloned().map(|y: pallas::Base| -y)
} else {
magnitude_mul.y.value()
magnitude_mul.y.value().cloned()
}
} else {
None
@ -182,7 +175,7 @@ impl Config {
Ok(EccPoint {
x: magnitude_mul.x,
y: CellValue::new(y_var, y_val),
y: y_var,
})
},
)?;
@ -199,7 +192,7 @@ impl Config {
if let (Some(magnitude), Some(sign)) = (scalar.magnitude.value(), scalar.sign.value()) {
let magnitude_is_valid =
magnitude <= pallas::Base::from_u64(0xFFFF_FFFF_FFFF_FFFFu64);
magnitude <= &pallas::Base::from_u64(0xFFFF_FFFF_FFFF_FFFFu64);
let sign_is_valid = sign * sign == pallas::Base::one();
if magnitude_is_valid && sign_is_valid {
let base: super::OrchardFixedBases = base.clone().into();
@ -211,7 +204,7 @@ impl Config {
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()
} else {
-pallas::Scalar::one()
@ -240,14 +233,17 @@ pub mod tests {
use group::Curve;
use halo2::{
arithmetic::CurveAffine,
circuit::{Chip, Layouter},
circuit::{AssignedCell, Chip, Layouter},
plonk::{Any, Error},
};
use pasta_curves::{arithmetic::FieldExt, pallas};
use crate::circuit::gadget::{
ecc::{chip::EccChip, FixedPointShort, NonIdentityPoint, Point},
utilities::{lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions},
ecc::{
chip::{EccChip, MagnitudeSign},
FixedPointShort, NonIdentityPoint, Point,
},
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
};
use crate::constants::load::ValueCommitV;
@ -266,7 +262,7 @@ pub mod tests {
mut layouter: impl Layouter<pallas::Base>,
magnitude: pallas::Base,
sign: pallas::Base,
) -> Result<(CellValue<pallas::Base>, CellValue<pallas::Base>), Error> {
) -> Result<MagnitudeSign, Error> {
let column = chip.config().advices[0];
let magnitude =
chip.load_private(layouter.namespace(|| "magnitude"), column, Some(magnitude))?;
@ -371,7 +367,9 @@ pub mod tests {
)?;
value_commit_v.mul(layouter.namespace(|| *name), magnitude_sign)?
};
assert!(result.inner().is_identity().unwrap());
if let Some(is_identity) = result.inner().is_identity() {
assert!(is_identity);
}
}
Ok(())
@ -379,10 +377,7 @@ pub mod tests {
#[test]
fn invalid_magnitude_sign() {
use crate::circuit::gadget::{
ecc::chip::EccConfig,
utilities::{CellValue, UtilitiesInstructions},
};
use crate::circuit::gadget::{ecc::chip::EccConfig, utilities::UtilitiesInstructions};
use halo2::{
circuit::{Layouter, SimpleFloorPlanner},
dev::{MockProver, VerifyFailure},
@ -398,7 +393,7 @@ pub mod tests {
}
impl UtilitiesInstructions<pallas::Base> for MyCircuit {
type Var = CellValue<pallas::Base>;
type Var = AssignedCell<pallas::Base, pallas::Base>;
}
impl Circuit<pallas::Base> for MyCircuit {

View File

@ -1,14 +1,19 @@
use super::{CellValue, EccPoint, NonIdentityEccPoint, Var};
use super::{EccPoint, NonIdentityEccPoint};
use group::prime::PrimeCurveAffine;
use halo2::{
circuit::Region,
circuit::{AssignedCell, Region},
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector, VirtualCells},
poly::Rotation,
};
use pasta_curves::{arithmetic::CurveAffine, pallas};
type Coordinates = (
AssignedCell<pallas::Base, pallas::Base>,
AssignedCell<pallas::Base, pallas::Base>,
);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Config {
q_point: Selector,
@ -76,7 +81,7 @@ impl Config {
value: Option<(pallas::Base, pallas::Base)>,
offset: usize,
region: &mut Region<'_, pallas::Base>,
) -> Result<(CellValue<pallas::Base>, CellValue<pallas::Base>), Error> {
) -> Result<Coordinates, Error> {
// Assign `x` value
let x_val = value.map(|value| value.0);
let x_var =
@ -87,10 +92,7 @@ impl Config {
let y_var =
region.assign_advice(|| "y", self.y, offset, || y_val.ok_or(Error::Synthesis))?;
Ok((
CellValue::<pallas::Base>::new(x_var, x_val),
CellValue::<pallas::Base>::new(y_var, y_val),
))
Ok((x_var, y_var))
}
/// Assigns a point that can be the identity.

View File

@ -1,18 +1,18 @@
//! Gadget and chips for the Poseidon algebraic hash function.
use std::array;
use std::convert::TryInto;
use std::fmt;
use halo2::{
arithmetic::FieldExt,
circuit::{Chip, Layouter},
circuit::{AssignedCell, Chip, Layouter},
plonk::Error,
};
mod pow5;
pub use pow5::{Pow5Chip, Pow5Config, StateWord};
use crate::circuit::gadget::utilities::CellValue;
use crate::primitives::poseidon::{ConstantLength, Domain, Spec, Sponge, SpongeState, State};
/// The set of circuit instructions required to use the Poseidon permutation.
@ -20,7 +20,7 @@ pub trait PoseidonInstructions<F: FieldExt, S: Spec<F, T, RATE>, const T: usize,
Chip<F>
{
/// Variable representing the word over which the Poseidon permutation operates.
type Word: Copy + fmt::Debug + From<CellValue<F>> + Into<CellValue<F>>;
type Word: Clone + fmt::Debug + From<AssignedCell<F, F>> + Into<AssignedCell<F, F>>;
/// Applies the Poseidon permutation to the given state.
fn permute(
@ -82,7 +82,7 @@ impl<
{
/// The word contained in this gadget.
pub fn inner(&self) -> PoseidonChip::Word {
self.inner
self.inner.clone()
}
/// Construct a [`Word`] gadget from the inner word.
@ -144,7 +144,13 @@ impl<
chip.initial_state(&mut layouter, &domain)
.map(|state| Duplex {
chip,
sponge: Sponge::Absorbing([None; RATE]),
sponge: Sponge::Absorbing(
(0..RATE)
.map(|_| None)
.collect::<Vec<_>>()
.try_into()
.unwrap(),
),
state,
domain,
})
@ -154,7 +160,7 @@ impl<
pub fn absorb(
&mut self,
mut layouter: impl Layouter<F>,
value: CellValue<F>,
value: AssignedCell<F, F>,
) -> Result<(), Error> {
match self.sponge {
Sponge::Absorbing(ref mut input) => {
@ -185,7 +191,7 @@ impl<
}
/// Squeezes an element from the sponge.
pub fn squeeze(&mut self, mut layouter: impl Layouter<F>) -> Result<CellValue<F>, Error> {
pub fn squeeze(&mut self, mut layouter: impl Layouter<F>) -> Result<AssignedCell<F, F>, Error> {
loop {
match self.sponge {
Sponge::Absorbing(ref input) => {
@ -205,7 +211,13 @@ impl<
}
// We've already squeezed out all available elements
self.sponge = Sponge::Absorbing([None; RATE]);
self.sponge = Sponge::Absorbing(
(0..RATE)
.map(|_| None)
.collect::<Vec<_>>()
.try_into()
.unwrap(),
);
}
}
}
@ -253,8 +265,8 @@ impl<
pub fn hash(
mut self,
mut layouter: impl Layouter<F>,
message: [CellValue<F>; L],
) -> Result<CellValue<F>, Error> {
message: [AssignedCell<F, F>; L],
) -> Result<AssignedCell<F, F>, Error> {
for (i, value) in array::IntoIter::new(message).enumerate() {
self.duplex
.absorb(layouter.namespace(|| format!("absorb_{}", i)), value)?;

View File

@ -3,13 +3,13 @@ use std::iter;
use halo2::{
arithmetic::FieldExt,
circuit::{Cell, Chip, Layouter, Region},
circuit::{AssignedCell, Cell, Chip, Layouter, Region},
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Fixed, Selector},
poly::Rotation,
};
use super::{PoseidonDuplexInstructions, PoseidonInstructions};
use crate::circuit::gadget::utilities::{CellValue, Var};
use crate::circuit::gadget::utilities::Var;
use crate::primitives::poseidon::{Domain, Mds, Spec, SpongeState, State};
/// Configuration for a [`Pow5Chip`].
@ -288,10 +288,7 @@ impl<F: FieldExt, S: Spec<F, WIDTH, RATE>, const WIDTH: usize, const RATE: usize
0,
value,
)?;
state.push(StateWord {
var,
value: Some(value),
});
state.push(StateWord(var));
Ok(())
};
@ -323,15 +320,15 @@ impl<F: FieldExt, S: Spec<F, WIDTH, RATE>, const WIDTH: usize, const RATE: usize
// Load the initial state into this region.
let load_state_word = |i: usize| {
let value = initial_state[i].value;
let var = region.assign_advice(
|| format!("load state_{}", i),
config.state[i],
0,
|| value.ok_or(Error::Synthesis),
)?;
region.constrain_equal(initial_state[i].var, var)?;
Ok(StateWord { var, value })
initial_state[i]
.0
.copy_advice(
|| format!("load state_{}", i),
&mut region,
config.state[i],
0,
)
.map(StateWord)
};
let initial_state: Result<Vec<_>, Error> =
(0..WIDTH).map(load_state_word).collect();
@ -341,49 +338,46 @@ impl<F: FieldExt, S: Spec<F, WIDTH, RATE>, const WIDTH: usize, const RATE: usize
// Load the input and padding into this region.
let load_input_word = |i: usize| {
let (constraint_var, value) = match (input[i], padding_values[i]) {
(Some(word), None) => (word.var, word.value),
(None, Some(padding_value)) => {
let padding_var = region.assign_fixed(
|| format!("load pad_{}", i),
config.rc_b[i],
1,
|| Ok(padding_value),
)?;
(padding_var, Some(padding_value))
}
let constraint_var = match (input[i].clone(), padding_values[i]) {
(Some(word), None) => word.0,
(None, Some(padding_value)) => region.assign_fixed(
|| format!("load pad_{}", i),
config.rc_b[i],
1,
|| Ok(padding_value),
)?,
_ => panic!("Input and padding don't match"),
};
let var = region.assign_advice(
|| format!("load input_{}", i),
config.state[i],
1,
|| value.ok_or(Error::Synthesis),
)?;
region.constrain_equal(constraint_var, var)?;
Ok(StateWord { var, value })
constraint_var
.copy_advice(
|| format!("load input_{}", i),
&mut region,
config.state[i],
1,
)
.map(StateWord)
};
let input: Result<Vec<_>, Error> = (0..RATE).map(load_input_word).collect();
let input = input?;
// Constrain the output.
let constrain_output_word = |i: usize| {
let value = initial_state[i].value.and_then(|initial_word| {
let value = initial_state[i].0.value().and_then(|initial_word| {
input
.get(i)
.map(|word| word.value)
.map(|word| word.0.value().cloned())
// The capacity element is never altered by the input.
.unwrap_or_else(|| Some(F::zero()))
.map(|input_word| initial_word + input_word)
.map(|input_word| *initial_word + input_word)
});
let var = region.assign_advice(
|| format!("load output_{}", i),
config.state[i],
2,
|| value.ok_or(Error::Synthesis),
)?;
Ok(StateWord { var, value })
region
.assign_advice(
|| format!("load output_{}", i),
config.state[i],
2,
|| value.ok_or(Error::Synthesis),
)
.map(StateWord)
};
let output: Result<Vec<_>, Error> = (0..WIDTH).map(constrain_output_word).collect();
@ -395,7 +389,7 @@ impl<F: FieldExt, S: Spec<F, WIDTH, RATE>, const WIDTH: usize, const RATE: usize
fn get_output(state: &State<Self::Word, WIDTH>) -> SpongeState<Self::Word, RATE> {
state[..RATE]
.iter()
.map(|word| Some(*word))
.map(|word| Some(word.clone()))
.collect::<Vec<_>>()
.try_into()
.unwrap()
@ -403,35 +397,28 @@ impl<F: FieldExt, S: Spec<F, WIDTH, RATE>, const WIDTH: usize, const RATE: usize
}
/// A word in the Poseidon state.
#[derive(Clone, Copy, Debug)]
pub struct StateWord<F: FieldExt> {
var: Cell,
value: Option<F>,
}
#[derive(Clone, Debug)]
pub struct StateWord<F: FieldExt>(AssignedCell<F, F>);
impl<F: FieldExt> From<StateWord<F>> for CellValue<F> {
fn from(state_word: StateWord<F>) -> CellValue<F> {
CellValue::new(state_word.var, state_word.value)
impl<F: FieldExt> From<StateWord<F>> for AssignedCell<F, F> {
fn from(state_word: StateWord<F>) -> AssignedCell<F, F> {
state_word.0
}
}
impl<F: FieldExt> From<CellValue<F>> for StateWord<F> {
fn from(cell_value: CellValue<F>) -> StateWord<F> {
StateWord::new(cell_value.cell(), cell_value.value())
impl<F: FieldExt> From<AssignedCell<F, F>> for StateWord<F> {
fn from(cell_value: AssignedCell<F, F>) -> StateWord<F> {
StateWord(cell_value)
}
}
impl<F: FieldExt> Var<F> for StateWord<F> {
fn new(var: Cell, value: Option<F>) -> Self {
Self { var, value }
}
fn cell(&self) -> Cell {
self.var
self.0.cell()
}
fn value(&self) -> Option<F> {
self.value
self.0.value().cloned()
}
}
@ -447,11 +434,11 @@ impl<F: FieldExt, const WIDTH: usize> Pow5State<F, WIDTH> {
offset: usize,
) -> Result<Self, Error> {
Self::round(region, config, round, offset, config.s_full, |_| {
let q = self
.0
.iter()
.enumerate()
.map(|(idx, word)| word.value.map(|v| v + config.round_constants[round][idx]));
let q = self.0.iter().enumerate().map(|(idx, word)| {
word.0
.value()
.map(|v| *v + config.round_constants[round][idx])
});
let r: Option<Vec<F>> = q.map(|q| q.map(|q| q.pow(&config.alpha))).collect();
let m = &config.m_reg;
let state = m.iter().map(|m_i| {
@ -475,7 +462,7 @@ impl<F: FieldExt, const WIDTH: usize> Pow5State<F, WIDTH> {
) -> Result<Self, Error> {
Self::round(region, config, round, offset, config.s_partial, |region| {
let m = &config.m_reg;
let p: Option<Vec<_>> = self.0.iter().map(|word| word.value).collect();
let p: Option<Vec<_>> = self.0.iter().map(|word| word.0.value().cloned()).collect();
let r: Option<Vec<_>> = p.map(|p| {
let r_0 = (p[0] + config.round_constants[round][0]).pow(&config.alpha);
@ -547,15 +534,10 @@ impl<F: FieldExt, const WIDTH: usize> Pow5State<F, WIDTH> {
initial_state: &State<StateWord<F>, WIDTH>,
) -> Result<Self, Error> {
let load_state_word = |i: usize| {
let value = initial_state[i].value;
let var = region.assign_advice(
|| format!("load state_{}", i),
config.state[i],
0,
|| value.ok_or(Error::Synthesis),
)?;
region.constrain_equal(initial_state[i].var, var)?;
Ok(StateWord { var, value })
initial_state[i]
.0
.copy_advice(|| format!("load state_{}", i), region, config.state[i], 0)
.map(StateWord)
};
let state: Result<Vec<_>, _> = (0..WIDTH).map(load_state_word).collect();
@ -597,7 +579,7 @@ impl<F: FieldExt, const WIDTH: usize> Pow5State<F, WIDTH> {
offset + 1,
|| value.ok_or(Error::Synthesis),
)?;
Ok(StateWord { var, value })
Ok(StateWord(var))
};
let next_state: Result<Vec<_>, _> = (0..WIDTH).map(next_state_word).collect();
@ -619,10 +601,7 @@ mod tests {
use super::{PoseidonInstructions, Pow5Chip, Pow5Config, StateWord};
use crate::{
circuit::gadget::{
poseidon::Hash,
utilities::{CellValue, Var},
},
circuit::gadget::poseidon::Hash,
primitives::poseidon::{self, ConstantLength, P128Pow5T3 as OrchardNullifier, Spec},
};
use std::convert::TryInto;
@ -674,7 +653,7 @@ mod tests {
0,
|| value.ok_or(Error::Synthesis),
)?;
Ok(StateWord { var, value })
Ok(StateWord(var))
};
let state: Result<Vec<_>, Error> = (0..WIDTH).map(state_word).collect();
@ -713,7 +692,7 @@ mod tests {
0,
|| Ok(expected_final_state[i]),
)?;
region.constrain_equal(final_state[i].var, var)
region.constrain_equal(final_state[i].0.cell(), var.cell())
};
for i in 0..(WIDTH) {
@ -791,13 +770,12 @@ mod tests {
|mut region| {
let message_word = |i: usize| {
let value = self.message.map(|message_vals| message_vals[i]);
let cell = region.assign_advice(
region.assign_advice(
|| format!("load message_{}", i),
config.state[i],
0,
|| value.ok_or(Error::Synthesis),
)?;
Ok(CellValue::new(cell, value))
)
};
let message: Result<Vec<_>, Error> = (0..L).map(message_word).collect();
@ -821,7 +799,7 @@ mod tests {
0,
|| self.output.ok_or(Error::Synthesis),
)?;
region.constrain_equal(output.cell(), expected_var)
region.constrain_equal(output.cell(), expected_var.cell())
},
)
}

View File

@ -32,7 +32,7 @@ pub trait SinsemillaInstructions<C: CurveAffine, const K: usize, const MAX_WORDS
///
/// For example, in the case `K = 10`, `NUM_BITS = 255`, we can fit
/// up to `N = 25` words in a single base field element.
type MessagePiece: Copy + Clone + Debug;
type MessagePiece: Clone + Debug;
/// A cumulative sum `z` is used to decompose a Sinsemilla message. It
/// produces intermediate values for each word in the message, such
@ -170,7 +170,7 @@ where
SinsemillaChip: SinsemillaInstructions<C, K, MAX_WORDS> + Clone + Debug + Eq,
{
fn inner(&self) -> SinsemillaChip::MessagePiece {
self.inner
self.inner.clone()
}
}

View File

@ -4,8 +4,7 @@ use super::{
};
use crate::{
circuit::gadget::{
ecc::chip::NonIdentityEccPoint,
utilities::{lookup_range_check::LookupRangeCheckConfig, CellValue, Var},
ecc::chip::NonIdentityEccPoint, utilities::lookup_range_check::LookupRangeCheckConfig,
},
constants::OrchardFixedBasesFull,
primitives::sinsemilla::{
@ -15,7 +14,7 @@ use crate::{
use halo2::{
arithmetic::{CurveAffine, FieldExt},
circuit::{Chip, Layouter},
circuit::{AssignedCell, Chip, Layouter},
plonk::{
Advice, Column, ConstraintSystem, Error, Expression, Fixed, Selector, TableColumn,
VirtualCells,
@ -239,14 +238,14 @@ impl SinsemillaChip {
impl SinsemillaInstructions<pallas::Affine, { sinsemilla::K }, { sinsemilla::C }>
for SinsemillaChip
{
type CellValue = CellValue<pallas::Base>;
type CellValue = AssignedCell<pallas::Base, pallas::Base>;
type Message = Message<pallas::Base, { sinsemilla::K }, { sinsemilla::C }>;
type MessagePiece = MessagePiece<pallas::Base, { sinsemilla::K }>;
type RunningSum = Vec<Self::CellValue>;
type X = CellValue<pallas::Base>;
type X = AssignedCell<pallas::Base, pallas::Base>;
type NonIdentityPoint = NonIdentityEccPoint;
type FixedPoints = OrchardFixedBasesFull;
@ -272,7 +271,7 @@ impl SinsemillaInstructions<pallas::Affine, { sinsemilla::K }, { sinsemilla::C }
)
},
)?;
Ok(MessagePiece::new(cell, field_elem, num_words))
Ok(MessagePiece::new(cell, num_words))
}
#[allow(non_snake_case)]

View File

@ -1,6 +1,7 @@
use super::super::SinsemillaInstructions;
use super::{CellValue, NonIdentityEccPoint, SinsemillaChip, Var};
use super::{NonIdentityEccPoint, SinsemillaChip};
use crate::primitives::sinsemilla::{self, lebs2ip_k, INV_TWO_POW_K, SINSEMILLA_S};
use halo2::circuit::AssignedCell;
use halo2::{
circuit::{Chip, Region},
plonk::Error,
@ -26,7 +27,13 @@ impl SinsemillaChip {
{ sinsemilla::K },
{ sinsemilla::C },
>>::Message,
) -> Result<(NonIdentityEccPoint, Vec<Vec<CellValue<pallas::Base>>>), Error> {
) -> Result<
(
NonIdentityEccPoint,
Vec<Vec<AssignedCell<pallas::Base, pallas::Base>>>,
),
Error,
> {
let config = self.config().clone();
let mut offset = 0;
@ -46,16 +53,13 @@ impl SinsemillaChip {
// Constrain the initial x_q to equal the x-coordinate of the domain's `Q`.
let mut x_a: X<pallas::Base> = {
let x_a = {
let cell =
region.assign_advice_from_constant(|| "fixed x_q", config.x_a, offset, x_q)?;
CellValue::new(cell, Some(x_q))
};
let x_a =
region.assign_advice_from_constant(|| "fixed x_q", config.x_a, offset, x_q)?;
x_a.into()
};
let mut zs_sum: Vec<Vec<CellValue<pallas::Base>>> = Vec::new();
let mut zs_sum: Vec<Vec<AssignedCell<pallas::Base, pallas::Base>>> = Vec::new();
// Hash each piece in the message.
for (idx, piece) in message.iter().enumerate() {
@ -102,7 +106,7 @@ impl SinsemillaChip {
)?;
}
CellValue::new(y_a_cell, y_a.0)
y_a_cell
};
#[cfg(test)]
@ -142,14 +146,14 @@ impl SinsemillaChip {
.chunks(K)
.fold(Q.to_curve(), |acc, chunk| (acc + S(chunk)) + acc);
let actual_point =
pallas::Affine::from_xy(x_a.value().unwrap(), y_a.value().unwrap()).unwrap();
pallas::Affine::from_xy(*x_a.value().unwrap(), *y_a.value().unwrap()).unwrap();
assert_eq!(expected_point.to_affine(), actual_point);
}
}
if let Some(x_a) = x_a.value() {
if let Some(y_a) = y_a.value() {
if x_a == pallas::Base::zero() || y_a == pallas::Base::zero() {
if x_a.is_zero_vartime() || y_a.is_zero_vartime() {
return Err(Error::Synthesis);
}
}
@ -183,7 +187,7 @@ impl SinsemillaChip {
(
X<pallas::Base>,
Y<pallas::Base>,
Vec<CellValue<pallas::Base>>,
Vec<AssignedCell<pallas::Base, pallas::Base>>,
),
Error,
> {
@ -264,14 +268,13 @@ impl SinsemillaChip {
let mut zs = Vec::with_capacity(piece.num_words() + 1);
// Copy message and initialize running sum `z` to decompose message in-circuit
let cell = region.assign_advice(
let initial_z = piece.cell_value().copy_advice(
|| "z_0 (copy of message piece)",
region,
config.bits,
offset,
|| piece.field_elem().ok_or(Error::Synthesis),
)?;
region.constrain_equal(piece.cell(), cell)?;
zs.push(CellValue::new(cell, piece.field_elem()));
zs.push(initial_z);
// Assign cumulative sum such that for 0 <= i < n,
// z_i = 2^K * z_{i + 1} + m_{i + 1}
@ -295,7 +298,7 @@ impl SinsemillaChip {
offset + idx + 1,
|| z.ok_or(Error::Synthesis),
)?;
zs.push(CellValue::new(cell, z))
zs.push(cell)
}
zs
@ -381,7 +384,7 @@ impl SinsemillaChip {
|| x_a_new.ok_or(Error::Synthesis),
)?;
CellValue::new(x_a_cell, x_a_new).into()
x_a_cell.into()
};
// Compute y_a for the next row.
@ -402,18 +405,18 @@ impl SinsemillaChip {
}
/// The x-coordinate of the accumulator in a Sinsemilla hash instance.
struct X<F: FieldExt>(CellValue<F>);
struct X<F: FieldExt>(AssignedCell<F, F>);
impl<F: FieldExt> From<CellValue<F>> for X<F> {
fn from(cell_value: CellValue<F>) -> Self {
impl<F: FieldExt> From<AssignedCell<F, F>> for X<F> {
fn from(cell_value: AssignedCell<F, F>) -> Self {
X(cell_value)
}
}
impl<F: FieldExt> Deref for X<F> {
type Target = CellValue<F>;
type Target = AssignedCell<F, F>;
fn deref(&self) -> &CellValue<F> {
fn deref(&self) -> &AssignedCell<F, F> {
&self.0
}
}

View File

@ -1,5 +1,5 @@
use halo2::{
circuit::Layouter,
circuit::{AssignedCell, Layouter},
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector},
poly::Rotation,
};
@ -8,7 +8,7 @@ use pasta_curves::{arithmetic::FieldExt, pallas};
use crate::{
circuit::gadget::{
ecc::{chip::EccChip, X},
utilities::{bitrange_subset, bool_check, copy, CellValue, Var},
utilities::{bitrange_subset, bool_check},
},
constants::T_P,
};
@ -225,8 +225,8 @@ impl CommitIvkConfig {
sinsemilla_chip: SinsemillaChip,
ecc_chip: EccChip,
mut layouter: impl Layouter<pallas::Base>,
ak: CellValue<pallas::Base>,
nk: CellValue<pallas::Base>,
ak: AssignedCell<pallas::Base, pallas::Base>,
nk: AssignedCell<pallas::Base, pallas::Base>,
rivk: Option<pallas::Scalar>,
) -> Result<X<pallas::Affine, EccChip>, Error> {
// <https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit>
@ -337,8 +337,8 @@ impl CommitIvkConfig {
domain.short_commit(layouter.namespace(|| "Hash ak||nk"), message, rivk)?
};
let z13_a = zs[0][13];
let z13_c = zs[2][13];
let z13_a = zs[0][13].clone();
let z13_c = zs[2][13].clone();
let (a_prime, z13_a_prime) = self.ak_canonicity(
layouter.namespace(|| "ak canonicity"),
@ -347,7 +347,7 @@ impl CommitIvkConfig {
let (b2_c_prime, z14_b2_c_prime) = self.nk_canonicity(
layouter.namespace(|| "nk canonicity"),
b_2,
b_2.clone(),
c.inner().cell_value(),
)?;
@ -384,8 +384,14 @@ impl CommitIvkConfig {
fn ak_canonicity(
&self,
mut layouter: impl Layouter<pallas::Base>,
a: CellValue<pallas::Base>,
) -> Result<(CellValue<pallas::Base>, CellValue<pallas::Base>), Error> {
a: AssignedCell<pallas::Base, pallas::Base>,
) -> Result<
(
AssignedCell<pallas::Base, pallas::Base>,
AssignedCell<pallas::Base, pallas::Base>,
),
Error,
> {
// `ak` = `a (250 bits) || b_0 (4 bits) || b_1 (1 bit)`
// - b_1 = 1 => b_0 = 0
// - b_1 = 1 => a < t_P
@ -406,10 +412,10 @@ impl CommitIvkConfig {
13,
false,
)?;
let a_prime = zs[0];
let a_prime = zs[0].clone();
assert_eq!(zs.len(), 14); // [z_0, z_1, ..., z13_a]
Ok((a_prime, zs[13]))
Ok((a_prime, zs[13].clone()))
}
#[allow(clippy::type_complexity)]
@ -417,9 +423,15 @@ impl CommitIvkConfig {
fn nk_canonicity(
&self,
mut layouter: impl Layouter<pallas::Base>,
b_2: CellValue<pallas::Base>,
c: CellValue<pallas::Base>,
) -> Result<(CellValue<pallas::Base>, CellValue<pallas::Base>), Error> {
b_2: AssignedCell<pallas::Base, pallas::Base>,
c: AssignedCell<pallas::Base, pallas::Base>,
) -> Result<
(
AssignedCell<pallas::Base, pallas::Base>,
AssignedCell<pallas::Base, pallas::Base>,
),
Error,
> {
// `nk` = `b_2 (5 bits) || c (240 bits) || d_0 (9 bits) || d_1 (1 bit)
// - d_1 = 1 => d_0 = 0
// - d_1 = 1 => b_2 + c * 2^5 < t_P
@ -443,10 +455,10 @@ impl CommitIvkConfig {
14,
false,
)?;
let b2_c_prime = zs[0];
let b2_c_prime = zs[0].clone();
assert_eq!(zs.len(), 15); // [z_0, z_1, ..., z14]
Ok((b2_c_prime, zs[14]))
Ok((b2_c_prime, zs[14].clone()))
}
// Assign cells for the canonicity gate.
@ -474,28 +486,24 @@ impl CommitIvkConfig {
{
let offset = 0;
// Copy in `ak`
copy(
&mut region,
|| "ak",
self.advices[0],
offset,
&gate_cells.ak,
)?;
gate_cells
.ak
.copy_advice(|| "ak", &mut region, self.advices[0], offset)?;
// Copy in `a`
copy(&mut region, || "a", self.advices[1], offset, &gate_cells.a)?;
gate_cells
.a
.copy_advice(|| "a", &mut region, self.advices[1], offset)?;
// Copy in `b`
copy(&mut region, || "b", self.advices[2], offset, &gate_cells.b)?;
gate_cells
.b
.copy_advice(|| "b", &mut region, self.advices[2], offset)?;
// Copy in `b_0`
copy(
&mut region,
|| "b_0",
self.advices[3],
offset,
&gate_cells.b_0,
)?;
gate_cells
.b_0
.copy_advice(|| "b_0", &mut region, self.advices[3], offset)?;
// Witness `b_1`
region.assign_advice(
@ -506,39 +514,32 @@ impl CommitIvkConfig {
)?;
// Copy in `b_2`
copy(
&mut region,
|| "b_2",
self.advices[5],
offset,
&gate_cells.b_2,
)?;
gate_cells
.b_2
.copy_advice(|| "b_2", &mut region, self.advices[5], offset)?;
// Copy in z13_a
copy(
&mut region,
gate_cells.z13_a.copy_advice(
|| "z13_a",
&mut region,
self.advices[6],
offset,
&gate_cells.z13_a,
)?;
// Copy in a_prime
copy(
&mut region,
gate_cells.a_prime.copy_advice(
|| "a_prime",
&mut region,
self.advices[7],
offset,
&gate_cells.a_prime,
)?;
// Copy in z13_a_prime
copy(
&mut region,
gate_cells.z13_a_prime.copy_advice(
|| "z13_a_prime",
&mut region,
self.advices[8],
offset,
&gate_cells.z13_a_prime,
)?;
}
@ -547,28 +548,24 @@ impl CommitIvkConfig {
let offset = 1;
// Copy in `nk`
copy(
&mut region,
|| "nk",
self.advices[0],
offset,
&gate_cells.nk,
)?;
gate_cells
.nk
.copy_advice(|| "nk", &mut region, self.advices[0], offset)?;
// Copy in `c`
copy(&mut region, || "c", self.advices[1], offset, &gate_cells.c)?;
gate_cells
.c
.copy_advice(|| "c", &mut region, self.advices[1], offset)?;
// Copy in `d`
copy(&mut region, || "d", self.advices[2], offset, &gate_cells.d)?;
gate_cells
.d
.copy_advice(|| "d", &mut region, self.advices[2], offset)?;
// Copy in `d_0`
copy(
&mut region,
|| "d_0",
self.advices[3],
offset,
&gate_cells.d_0,
)?;
gate_cells
.d_0
.copy_advice(|| "d_0", &mut region, self.advices[3], offset)?;
// Witness `d_1`
region.assign_advice(
@ -579,30 +576,27 @@ impl CommitIvkConfig {
)?;
// Copy in z13_c
copy(
&mut region,
gate_cells.z13_c.copy_advice(
|| "z13_c",
&mut region,
self.advices[6],
offset,
&gate_cells.z13_c,
)?;
// Copy in b2_c_prime
copy(
&mut region,
gate_cells.b2_c_prime.copy_advice(
|| "b2_c_prime",
&mut region,
self.advices[7],
offset,
&gate_cells.b2_c_prime,
)?;
// Copy in z14_b2_c_prime
copy(
&mut region,
gate_cells.z14_b2_c_prime.copy_advice(
|| "z14_b2_c_prime",
&mut region,
self.advices[8],
offset,
&gate_cells.z14_b2_c_prime,
)?;
}
@ -614,23 +608,23 @@ impl CommitIvkConfig {
// Cells used in the canonicity gate.
struct GateCells {
a: CellValue<pallas::Base>,
b: CellValue<pallas::Base>,
c: CellValue<pallas::Base>,
d: CellValue<pallas::Base>,
ak: CellValue<pallas::Base>,
nk: CellValue<pallas::Base>,
b_0: CellValue<pallas::Base>,
a: AssignedCell<pallas::Base, pallas::Base>,
b: AssignedCell<pallas::Base, pallas::Base>,
c: AssignedCell<pallas::Base, pallas::Base>,
d: AssignedCell<pallas::Base, pallas::Base>,
ak: AssignedCell<pallas::Base, pallas::Base>,
nk: AssignedCell<pallas::Base, pallas::Base>,
b_0: AssignedCell<pallas::Base, pallas::Base>,
b_1: Option<pallas::Base>,
b_2: CellValue<pallas::Base>,
d_0: CellValue<pallas::Base>,
b_2: AssignedCell<pallas::Base, pallas::Base>,
d_0: AssignedCell<pallas::Base, pallas::Base>,
d_1: Option<pallas::Base>,
z13_a: CellValue<pallas::Base>,
a_prime: CellValue<pallas::Base>,
z13_a_prime: CellValue<pallas::Base>,
z13_c: CellValue<pallas::Base>,
b2_c_prime: CellValue<pallas::Base>,
z14_b2_c_prime: CellValue<pallas::Base>,
z13_a: AssignedCell<pallas::Base, pallas::Base>,
a_prime: AssignedCell<pallas::Base, pallas::Base>,
z13_a_prime: AssignedCell<pallas::Base, pallas::Base>,
z13_c: AssignedCell<pallas::Base, pallas::Base>,
b2_c_prime: AssignedCell<pallas::Base, pallas::Base>,
z14_b2_c_prime: AssignedCell<pallas::Base, pallas::Base>,
}
#[cfg(test)]
@ -640,16 +634,14 @@ mod tests {
circuit::gadget::{
ecc::chip::{EccChip, EccConfig},
sinsemilla::chip::SinsemillaChip,
utilities::{
lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions, Var,
},
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
},
constants::{COMMIT_IVK_PERSONALIZATION, L_ORCHARD_BASE, T_Q},
primitives::sinsemilla::CommitDomain,
};
use ff::PrimeFieldBits;
use halo2::{
circuit::{Layouter, SimpleFloorPlanner},
circuit::{AssignedCell, Layouter, SimpleFloorPlanner},
dev::MockProver,
plonk::{Circuit, ConstraintSystem, Error},
};
@ -666,7 +658,7 @@ mod tests {
}
impl UtilitiesInstructions<pallas::Base> for MyCircuit {
type Var = CellValue<pallas::Base>;
type Var = AssignedCell<pallas::Base, pallas::Base>;
}
impl Circuit<pallas::Base> for MyCircuit {
@ -803,7 +795,7 @@ mod tests {
.unwrap()
};
assert_eq!(expected_ivk, ivk.inner().value().unwrap());
assert_eq!(&expected_ivk, ivk.inner().value().unwrap());
Ok(())
}

View File

@ -139,7 +139,7 @@ pub mod tests {
use crate::{
circuit::gadget::{
sinsemilla::chip::{SinsemillaChip, SinsemillaHashDomains},
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions, Var},
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
},
constants::MERKLE_DEPTH_ORCHARD,
note::commitment::ExtractedNoteCommitment,
@ -266,7 +266,7 @@ pub mod tests {
};
// Check the computed final root against the expected final root.
assert_eq!(computed_final_root.value().unwrap(), final_root.inner());
assert_eq!(computed_final_root.value().unwrap(), &final_root.inner());
}
Ok(())

View File

@ -1,5 +1,5 @@
use halo2::{
circuit::{Chip, Layouter},
circuit::{AssignedCell, Chip, Layouter},
plonk::{Advice, Column, ConstraintSystem, Error, Selector},
poly::Rotation,
};
@ -15,7 +15,7 @@ use crate::{
circuit::gadget::utilities::{
bitrange_subset,
cond_swap::{CondSwapChip, CondSwapConfig, CondSwapInstructions},
copy, CellValue, UtilitiesInstructions, Var,
UtilitiesInstructions,
},
constants::{L_ORCHARD_BASE, MERKLE_DEPTH_ORCHARD},
primitives::sinsemilla,
@ -185,7 +185,7 @@ impl MerkleInstructions<pallas::Affine, MERKLE_DEPTH_ORCHARD, { sinsemilla::K },
let a = {
let a = {
// a_0 = l
let a_0 = bitrange_subset(pallas::Base::from_u64(l as u64), 0..10);
let a_0 = bitrange_subset(&pallas::Base::from_u64(l as u64), 0..10);
// a_1 = (bits 0..=239 of `left`)
let a_1 = left.value().map(|value| bitrange_subset(value, 0..240));
@ -257,10 +257,10 @@ impl MerkleInstructions<pallas::Affine, MERKLE_DEPTH_ORCHARD, { sinsemilla::K },
let (point, zs) = self.hash_to_point(
layouter.namespace(|| format!("hash at l = {}", l)),
Q,
vec![a, b, c].into(),
vec![a.clone(), b.clone(), c.clone()].into(),
)?;
let z1_a = zs[0][1];
let z1_b = zs[1][1];
let z1_a = zs[0][1].clone();
let z1_b = zs[1][1].clone();
// Check that the pieces have been decomposed properly.
/*
@ -287,43 +287,28 @@ impl MerkleInstructions<pallas::Affine, MERKLE_DEPTH_ORCHARD, { sinsemilla::K },
// Offset 0
// Copy and assign `a` at the correct position.
copy(
&mut region,
|| "copy a",
config.advices[0],
0,
&a.cell_value(),
)?;
a.cell_value()
.copy_advice(|| "copy a", &mut region, config.advices[0], 0)?;
// Copy and assign `b` at the correct position.
copy(
&mut region,
|| "copy b",
config.advices[1],
0,
&b.cell_value(),
)?;
b.cell_value()
.copy_advice(|| "copy b", &mut region, config.advices[1], 0)?;
// Copy and assign `c` at the correct position.
copy(
&mut region,
|| "copy c",
config.advices[2],
0,
&c.cell_value(),
)?;
c.cell_value()
.copy_advice(|| "copy c", &mut region, config.advices[2], 0)?;
// Copy and assign the left node at the correct position.
copy(&mut region, || "left", config.advices[3], 0, &left)?;
left.copy_advice(|| "left", &mut region, config.advices[3], 0)?;
// Copy and assign the right node at the correct position.
copy(&mut region, || "right", config.advices[4], 0, &right)?;
right.copy_advice(|| "right", &mut region, config.advices[4], 0)?;
// Offset 1
// Copy and assign z_1 of SinsemillaHash(a) = a_1
copy(&mut region, || "z1_a", config.advices[0], 1, &z1_a)?;
z1_a.copy_advice(|| "z1_a", &mut region, config.advices[0], 1)?;
// Copy and assign z_1 of SinsemillaHash(b) = b_1
copy(&mut region, || "z1_b", config.advices[1], 1, &z1_b)?;
z1_b.copy_advice(|| "z1_b", &mut region, config.advices[1], 1)?;
// Copy `b_1`, which has been constrained to be a 5-bit value
copy(&mut region, || "b_1", config.advices[2], 1, &b_1)?;
b_1.copy_advice(|| "b_1", &mut region, config.advices[2], 1)?;
// Copy `b_2`, which has been constrained to be a 5-bit value
copy(&mut region, || "b_2", config.advices[3], 1, &b_2)?;
b_2.copy_advice(|| "b_2", &mut region, config.advices[3], 1)?;
Ok(())
},
@ -372,7 +357,7 @@ impl MerkleInstructions<pallas::Affine, MERKLE_DEPTH_ORCHARD, { sinsemilla::K },
}
impl UtilitiesInstructions<pallas::Base> for MerkleChip {
type Var = CellValue<pallas::Base>;
type Var = AssignedCell<pallas::Base, pallas::Base>;
}
impl CondSwapInstructions<pallas::Base> for MerkleChip {

View File

@ -1,7 +1,9 @@
//! Gadget and chips for the Sinsemilla hash function.
use crate::circuit::gadget::utilities::{CellValue, Var};
use ff::PrimeFieldBits;
use halo2::{arithmetic::FieldExt, circuit::Cell};
use halo2::{
arithmetic::FieldExt,
circuit::{AssignedCell, Cell},
};
use std::fmt::Debug;
/// A [`Message`] composed of several [`MessagePiece`]s.
@ -32,17 +34,16 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize, const MAX_WORDS: usize> std::
///
/// The piece must fit within a base field element, which means its length
/// cannot exceed the base field's `NUM_BITS`.
#[derive(Copy, Clone, Debug)]
#[derive(Clone, Debug)]
pub struct MessagePiece<F: FieldExt, const K: usize> {
cell_value: CellValue<F>,
cell_value: AssignedCell<F, F>,
/// The number of K-bit words in this message piece.
num_words: usize,
}
impl<F: FieldExt + PrimeFieldBits, const K: usize> MessagePiece<F, K> {
pub fn new(cell: Cell, field_elem: Option<F>, num_words: usize) -> Self {
pub fn new(cell_value: AssignedCell<F, F>, num_words: usize) -> Self {
assert!(num_words * K < F::NUM_BITS as usize);
let cell_value = CellValue::new(cell, field_elem);
Self {
cell_value,
num_words,
@ -58,10 +59,10 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> MessagePiece<F, K> {
}
pub fn field_elem(&self) -> Option<F> {
self.cell_value.value()
self.cell_value.value().cloned()
}
pub fn cell_value(&self) -> CellValue<F> {
self.cell_value
pub fn cell_value(&self) -> AssignedCell<F, F> {
self.cell_value.clone()
}
}

View File

@ -1,5 +1,5 @@
use halo2::{
circuit::Layouter,
circuit::{AssignedCell, Layouter},
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Selector},
poly::Rotation,
};
@ -11,7 +11,7 @@ use crate::{
chip::{EccChip, NonIdentityEccPoint},
Point,
},
utilities::{bitrange_subset, bool_check, copy, CellValue, Var},
utilities::{bitrange_subset, bool_check},
},
constants::T_P,
};
@ -21,6 +21,13 @@ use super::{
CommitDomain, Message, MessagePiece,
};
/// The values of the running sum at the start and end of the range being used for a
/// canonicity check.
type CanonicityBounds = (
AssignedCell<pallas::Base, pallas::Base>,
AssignedCell<pallas::Base, pallas::Base>,
);
/*
<https://zips.z.cash/protocol/nu5.pdf#concretesinsemillacommit>
We need to hash g_d || pk_d || i2lebsp_{64}(v) || rho || psi,
@ -525,13 +532,16 @@ impl NoteCommitConfig {
ecc_chip: EccChip,
g_d: &NonIdentityEccPoint,
pk_d: &NonIdentityEccPoint,
value: CellValue<pallas::Base>,
rho: CellValue<pallas::Base>,
psi: CellValue<pallas::Base>,
// TODO: Set V to Orchard value type
value: AssignedCell<pallas::Base, pallas::Base>,
rho: AssignedCell<pallas::Base, pallas::Base>,
psi: AssignedCell<pallas::Base, pallas::Base>,
rcm: Option<pallas::Scalar>,
) -> Result<Point<pallas::Affine, EccChip>, Error> {
let (gd_x, gd_y) = (g_d.x().value(), g_d.y().value());
let (pkd_x, pkd_y) = (pk_d.x().value(), pk_d.y().value());
let (gd_x, gd_y) = (g_d.x(), g_d.y());
let (pkd_x, pkd_y) = (pk_d.x(), pk_d.y());
let (gd_x, gd_y) = (gd_x.value(), gd_y.value());
let (pkd_x, pkd_y) = (pkd_x.value(), pkd_y.value());
let value_val = value.value();
let rho_val = rho.value();
let psi_val = psi.value();
@ -738,13 +748,13 @@ impl NoteCommitConfig {
)?
};
let z13_a = zs[0][13];
let z13_c = zs[2][13];
let z1_d = zs[3][1];
let z13_f = zs[5][13];
let z1_g = zs[6][1];
let g_2 = z1_g;
let z13_g = zs[6][13];
let z13_a = zs[0][13].clone();
let z13_c = zs[2][13].clone();
let z1_d = zs[3][1].clone();
let z13_f = zs[5][13].clone();
let z1_g = zs[6][1].clone();
let g_2 = z1_g.clone();
let z13_g = zs[6][13].clone();
let (a_prime, z13_a_prime) = self.canon_bitshift_130(
layouter.namespace(|| "x(g_d) canonicity"),
@ -753,18 +763,18 @@ impl NoteCommitConfig {
let (b3_c_prime, z14_b3_c_prime) = self.pkd_x_canonicity(
layouter.namespace(|| "x(pk_d) canonicity"),
b_3,
b_3.clone(),
c.inner().cell_value(),
)?;
let (e1_f_prime, z14_e1_f_prime) = self.rho_canonicity(
layouter.namespace(|| "rho canonicity"),
e_1,
e_1.clone(),
f.inner().cell_value(),
)?;
let (g1_g2_prime, z13_g1_g2_prime) =
self.psi_canonicity(layouter.namespace(|| "psi canonicity"), g_1, g_2)?;
self.psi_canonicity(layouter.namespace(|| "psi canonicity"), g_1.clone(), g_2)?;
let gate_cells = GateCells {
a: a.inner().cell_value(),
@ -814,13 +824,12 @@ impl NoteCommitConfig {
Ok(cm)
}
#[allow(clippy::type_complexity)]
// A canonicity check helper used in checking x(g_d), y(g_d), and y(pk_d).
fn canon_bitshift_130(
&self,
mut layouter: impl Layouter<pallas::Base>,
a: CellValue<pallas::Base>,
) -> Result<(CellValue<pallas::Base>, CellValue<pallas::Base>), Error> {
a: AssignedCell<pallas::Base, pallas::Base>,
) -> Result<CanonicityBounds, Error> {
// element = `a (250 bits) || b_0 (4 bits) || b_1 (1 bit)`
// - b_1 = 1 => b_0 = 0
// - b_1 = 1 => a < t_P
@ -841,19 +850,19 @@ impl NoteCommitConfig {
13,
false,
)?;
let a_prime = zs[0];
let a_prime = zs[0].clone();
assert_eq!(zs.len(), 14); // [z_0, z_1, ..., z_13]
Ok((a_prime, zs[13]))
Ok((a_prime, zs[13].clone()))
}
// Check canonicity of `x(pk_d)` encoding
fn pkd_x_canonicity(
&self,
mut layouter: impl Layouter<pallas::Base>,
b_3: CellValue<pallas::Base>,
c: CellValue<pallas::Base>,
) -> Result<(CellValue<pallas::Base>, CellValue<pallas::Base>), Error> {
b_3: AssignedCell<pallas::Base, pallas::Base>,
c: AssignedCell<pallas::Base, pallas::Base>,
) -> Result<CanonicityBounds, Error> {
// `x(pk_d)` = `b_3 (4 bits) || c (250 bits) || d_0 (1 bit)`
// - d_0 = 1 => b_3 + 2^4 c < t_P
// - 0 ≤ b_3 + 2^4 c < 2^134
@ -880,20 +889,19 @@ impl NoteCommitConfig {
14,
false,
)?;
let b3_c_prime = zs[0];
let b3_c_prime = zs[0].clone();
assert_eq!(zs.len(), 15); // [z_0, z_1, ..., z_13, z_14]
Ok((b3_c_prime, zs[14]))
Ok((b3_c_prime, zs[14].clone()))
}
#[allow(clippy::type_complexity)]
// Check canonicity of `rho` encoding
fn rho_canonicity(
&self,
mut layouter: impl Layouter<pallas::Base>,
e_1: CellValue<pallas::Base>,
f: CellValue<pallas::Base>,
) -> Result<(CellValue<pallas::Base>, CellValue<pallas::Base>), Error> {
e_1: AssignedCell<pallas::Base, pallas::Base>,
f: AssignedCell<pallas::Base, pallas::Base>,
) -> Result<CanonicityBounds, Error> {
// `rho` = `e_1 (4 bits) || f (250 bits) || g_0 (1 bit)`
// - g_0 = 1 => e_1 + 2^4 f < t_P
// - 0 ≤ e_1 + 2^4 f < 2^134
@ -920,19 +928,19 @@ impl NoteCommitConfig {
14,
false,
)?;
let e1_f_prime = zs[0];
let e1_f_prime = zs[0].clone();
assert_eq!(zs.len(), 15); // [z_0, z_1, ..., z_13, z_14]
Ok((e1_f_prime, zs[14]))
Ok((e1_f_prime, zs[14].clone()))
}
// Check canonicity of `psi` encoding
fn psi_canonicity(
&self,
mut layouter: impl Layouter<pallas::Base>,
g_1: CellValue<pallas::Base>,
g_2: CellValue<pallas::Base>,
) -> Result<(CellValue<pallas::Base>, CellValue<pallas::Base>), Error> {
g_1: AssignedCell<pallas::Base, pallas::Base>,
g_2: AssignedCell<pallas::Base, pallas::Base>,
) -> Result<CanonicityBounds, Error> {
// `psi` = `g_1 (9 bits) || g_2 (240 bits) || h_0 (5 bits) || h_1 (1 bit)`
// - h_1 = 1 => (h_0 = 0) ∧ (g_1 + 2^9 g_2 < t_P)
// - 0 ≤ g_1 + 2^9 g_2 < 2^130
@ -957,10 +965,10 @@ impl NoteCommitConfig {
13,
false,
)?;
let g1_g2_prime = zs[0];
let g1_g2_prime = zs[0].clone();
assert_eq!(zs.len(), 14); // [z_0, z_1, ..., z_13]
Ok((g1_g2_prime, zs[13]))
Ok((g1_g2_prime, zs[13].clone()))
}
// Check canonicity of y-coordinate given its LSB as a value.
@ -968,9 +976,9 @@ impl NoteCommitConfig {
fn y_canonicity(
&self,
mut layouter: impl Layouter<pallas::Base>,
y: CellValue<pallas::Base>,
y: AssignedCell<pallas::Base, pallas::Base>,
lsb: Option<pallas::Base>,
) -> Result<CellValue<pallas::Base>, Error> {
) -> Result<AssignedCell<pallas::Base, pallas::Base>, Error> {
// Decompose the field element
// y = LSB || k_0 || k_1 || k_2 || k_3
// = (bit 0) || (bits 1..=9) || (bits 10..=249) || (bits 250..=253) || (bit 254)
@ -1010,13 +1018,15 @@ impl NoteCommitConfig {
25,
true,
)?;
(zs[0], zs[1], zs[13])
(zs[0].clone(), zs[1].clone(), zs[13].clone())
};
// Decompose j_prime = j + 2^130 - t_P using 13 ten-bit lookups.
// We can reuse the canon_bitshift_130 logic here.
let (j_prime, z13_j_prime) =
self.canon_bitshift_130(layouter.namespace(|| "j_prime = j + 2^130 - t_P"), j)?;
let (j_prime, z13_j_prime) = self.canon_bitshift_130(
layouter.namespace(|| "j_prime = j + 2^130 - t_P"),
j.clone(),
)?;
/*
@ -1037,21 +1047,18 @@ impl NoteCommitConfig {
let offset = 0;
// Copy y.
copy(&mut region, || "copy y", self.advices[5], offset, &y)?;
y.copy_advice(|| "copy y", &mut region, self.advices[5], offset)?;
// Witness LSB.
let lsb = {
let cell = region.assign_advice(
|| "witness LSB",
self.advices[6],
offset,
|| lsb.ok_or(Error::Synthesis),
)?;
CellValue::new(cell, lsb)
};
let lsb = region.assign_advice(
|| "witness LSB",
self.advices[6],
offset,
|| lsb.ok_or(Error::Synthesis),
)?;
// Witness k_0.
copy(&mut region, || "copy k_0", self.advices[7], offset, &k_0)?;
k_0.copy_advice(|| "copy k_0", &mut region, self.advices[7], offset)?;
// Copy k_2.
copy(&mut region, || "copy k_2", self.advices[8], offset, &k_2)?;
k_2.copy_advice(|| "copy k_2", &mut region, self.advices[8], offset)?;
// Witness k_3.
region.assign_advice(
|| "witness k_3",
@ -1068,32 +1075,19 @@ impl NoteCommitConfig {
let offset = 1;
// Copy j.
copy(&mut region, || "copy j", self.advices[5], offset, &j)?;
j.copy_advice(|| "copy j", &mut region, self.advices[5], offset)?;
// Copy z1_j.
copy(&mut region, || "copy z1_j", self.advices[6], offset, &z1_j)?;
z1_j.copy_advice(|| "copy z1_j", &mut region, self.advices[6], offset)?;
// Copy z13_j.
copy(
&mut region,
|| "copy z13_j",
self.advices[7],
offset,
&z13_j,
)?;
z13_j.copy_advice(|| "copy z13_j", &mut region, self.advices[7], offset)?;
// Copy j_prime.
copy(
&mut region,
|| "copy j_prime",
self.advices[8],
offset,
&j_prime,
)?;
j_prime.copy_advice(|| "copy j_prime", &mut region, self.advices[8], offset)?;
// Copy z13_j_prime.
copy(
&mut region,
z13_j_prime.copy_advice(
|| "copy z13_j_prime",
&mut region,
self.advices[9],
offset,
&z13_j_prime,
)?;
}
@ -1122,20 +1116,23 @@ impl NoteCommitConfig {
|mut region| {
self.q_notecommit_b.enable(&mut region, 0)?;
copy(&mut region, || "b", col_l, 0, &gate_cells.b)?;
copy(&mut region, || "b_0", col_m, 0, &gate_cells.b_0)?;
let b_1 = {
let cell = region.assign_advice(
|| "b_1",
col_r,
0,
|| gate_cells.b_1.ok_or(Error::Synthesis),
)?;
CellValue::new(cell, gate_cells.b_1)
};
gate_cells.b.copy_advice(|| "b", &mut region, col_l, 0)?;
gate_cells
.b_0
.copy_advice(|| "b_0", &mut region, col_m, 0)?;
let b_1 = region.assign_advice(
|| "b_1",
col_r,
0,
|| gate_cells.b_1.ok_or(Error::Synthesis),
)?;
copy(&mut region, || "b_2", col_m, 1, &gate_cells.b_2)?;
copy(&mut region, || "b_3", col_r, 1, &gate_cells.b_3)?;
gate_cells
.b_2
.copy_advice(|| "b_2", &mut region, col_m, 1)?;
gate_cells
.b_3
.copy_advice(|| "b_3", &mut region, col_r, 1)?;
Ok(b_1)
},
@ -1150,20 +1147,23 @@ impl NoteCommitConfig {
|mut region| {
self.q_notecommit_d.enable(&mut region, 0)?;
copy(&mut region, || "d", col_l, 0, &gate_cells.d)?;
let d_0 = {
let cell = region.assign_advice(
|| "d_0",
col_m,
0,
|| gate_cells.d_0.ok_or(Error::Synthesis),
)?;
CellValue::new(cell, gate_cells.d_0)
};
copy(&mut region, || "d_1", col_r, 0, &gate_cells.d_1)?;
gate_cells.d.copy_advice(|| "d", &mut region, col_l, 0)?;
let d_0 = region.assign_advice(
|| "d_0",
col_m,
0,
|| gate_cells.d_0.ok_or(Error::Synthesis),
)?;
gate_cells
.d_1
.copy_advice(|| "d_1", &mut region, col_r, 0)?;
copy(&mut region, || "d_2", col_m, 1, &gate_cells.d_2)?;
copy(&mut region, || "d_3 = z1_d", col_r, 1, &gate_cells.z1_d)?;
gate_cells
.d_2
.copy_advice(|| "d_2", &mut region, col_m, 1)?;
gate_cells
.z1_d
.copy_advice(|| "d_3 = z1_d", &mut region, col_r, 1)?;
Ok(d_0)
},
@ -1177,9 +1177,13 @@ impl NoteCommitConfig {
|mut region| {
self.q_notecommit_e.enable(&mut region, 0)?;
copy(&mut region, || "e", col_l, 0, &gate_cells.e)?;
copy(&mut region, || "e_0", col_m, 0, &gate_cells.e_0)?;
copy(&mut region, || "e_1", col_r, 0, &gate_cells.e_1)?;
gate_cells.e.copy_advice(|| "e", &mut region, col_l, 0)?;
gate_cells
.e_0
.copy_advice(|| "e_0", &mut region, col_m, 0)?;
gate_cells
.e_1
.copy_advice(|| "e_1", &mut region, col_r, 0)?;
Ok(())
},
@ -1194,19 +1198,20 @@ impl NoteCommitConfig {
|mut region| {
self.q_notecommit_g.enable(&mut region, 0)?;
copy(&mut region, || "g", col_l, 0, &gate_cells.g)?;
let g_0 = {
let cell = region.assign_advice(
|| "g_0",
col_m,
0,
|| gate_cells.g_0.ok_or(Error::Synthesis),
)?;
CellValue::new(cell, gate_cells.g_0)
};
gate_cells.g.copy_advice(|| "g", &mut region, col_l, 0)?;
let g_0 = region.assign_advice(
|| "g_0",
col_m,
0,
|| gate_cells.g_0.ok_or(Error::Synthesis),
)?;
copy(&mut region, || "g_1", col_l, 1, &gate_cells.g_1)?;
copy(&mut region, || "g_2 = z1_g", col_m, 1, &gate_cells.z1_g)?;
gate_cells
.g_1
.copy_advice(|| "g_1", &mut region, col_l, 1)?;
gate_cells
.z1_g
.copy_advice(|| "g_2 = z1_g", &mut region, col_m, 1)?;
Ok(g_0)
},
@ -1220,17 +1225,16 @@ impl NoteCommitConfig {
|mut region| {
self.q_notecommit_h.enable(&mut region, 0)?;
copy(&mut region, || "h", col_l, 0, &gate_cells.h)?;
copy(&mut region, || "h_0", col_m, 0, &gate_cells.h_0)?;
let h_1 = {
let cell = region.assign_advice(
|| "h_1",
col_r,
0,
|| gate_cells.h_1.ok_or(Error::Synthesis),
)?;
CellValue::new(cell, gate_cells.h_1)
};
gate_cells.h.copy_advice(|| "h", &mut region, col_l, 0)?;
gate_cells
.h_0
.copy_advice(|| "h_0", &mut region, col_m, 0)?;
let h_1 = region.assign_advice(
|| "h_1",
col_r,
0,
|| gate_cells.h_1.ok_or(Error::Synthesis),
)?;
Ok(h_1)
},
@ -1243,22 +1247,26 @@ impl NoteCommitConfig {
layouter.assign_region(
|| "NoteCommit input g_d",
|mut region| {
copy(&mut region, || "gd_x", col_l, 0, &gate_cells.gd_x)?;
gate_cells
.gd_x
.copy_advice(|| "gd_x", &mut region, col_l, 0)?;
copy(&mut region, || "b_0", col_m, 0, &gate_cells.b_0)?;
copy(&mut region, || "b_1", col_m, 1, &b_1)?;
gate_cells
.b_0
.copy_advice(|| "b_0", &mut region, col_m, 0)?;
b_1.copy_advice(|| "b_1", &mut region, col_m, 1)?;
copy(&mut region, || "a", col_r, 0, &gate_cells.a)?;
copy(&mut region, || "a_prime", col_r, 1, &gate_cells.a_prime)?;
gate_cells.a.copy_advice(|| "a", &mut region, col_r, 0)?;
gate_cells
.a_prime
.copy_advice(|| "a_prime", &mut region, col_r, 1)?;
copy(&mut region, || "z13_a", col_z, 0, &gate_cells.z13_a)?;
copy(
&mut region,
|| "z13_a_prime",
col_z,
1,
&gate_cells.z13_a_prime,
)?;
gate_cells
.z13_a
.copy_advice(|| "z13_a", &mut region, col_z, 0)?;
gate_cells
.z13_a_prime
.copy_advice(|| "z13_a_prime", &mut region, col_z, 1)?;
self.q_notecommit_g_d.enable(&mut region, 0)
},
@ -1271,27 +1279,28 @@ impl NoteCommitConfig {
layouter.assign_region(
|| "NoteCommit input pk_d",
|mut region| {
copy(&mut region, || "pkd_x", col_l, 0, &gate_cells.pkd_x)?;
gate_cells
.pkd_x
.copy_advice(|| "pkd_x", &mut region, col_l, 0)?;
copy(&mut region, || "b_3", col_m, 0, &gate_cells.b_3)?;
copy(&mut region, || "d_0", col_m, 1, &d_0)?;
gate_cells
.b_3
.copy_advice(|| "b_3", &mut region, col_m, 0)?;
d_0.copy_advice(|| "d_0", &mut region, col_m, 1)?;
copy(&mut region, || "c", col_r, 0, &gate_cells.c)?;
copy(
&mut region,
|| "b3_c_prime",
col_r,
1,
&gate_cells.b3_c_prime,
)?;
gate_cells.c.copy_advice(|| "c", &mut region, col_r, 0)?;
gate_cells
.b3_c_prime
.copy_advice(|| "b3_c_prime", &mut region, col_r, 1)?;
copy(&mut region, || "z13_c", col_z, 0, &gate_cells.z13_c)?;
copy(
&mut region,
gate_cells
.z13_c
.copy_advice(|| "z13_c", &mut region, col_z, 0)?;
gate_cells.z14_b3_c_prime.copy_advice(
|| "z14_b3_c_prime",
&mut region,
col_z,
1,
&gate_cells.z14_b3_c_prime,
)?;
self.q_notecommit_pk_d.enable(&mut region, 0)
@ -1302,10 +1311,18 @@ impl NoteCommitConfig {
layouter.assign_region(
|| "NoteCommit input value",
|mut region| {
copy(&mut region, || "value", col_l, 0, &gate_cells.value)?;
copy(&mut region, || "d_2", col_m, 0, &gate_cells.d_2)?;
copy(&mut region, || "d3 = z1_d", col_r, 0, &gate_cells.z1_d)?;
copy(&mut region, || "e_0", col_z, 0, &gate_cells.e_0)?;
gate_cells
.value
.copy_advice(|| "value", &mut region, col_l, 0)?;
gate_cells
.d_2
.copy_advice(|| "d_2", &mut region, col_m, 0)?;
gate_cells
.z1_d
.copy_advice(|| "d3 = z1_d", &mut region, col_r, 0)?;
gate_cells
.e_0
.copy_advice(|| "e_0", &mut region, col_z, 0)?;
self.q_notecommit_value.enable(&mut region, 0)
},
@ -1318,27 +1335,28 @@ impl NoteCommitConfig {
layouter.assign_region(
|| "NoteCommit input rho",
|mut region| {
copy(&mut region, || "rho", col_l, 0, &gate_cells.rho)?;
gate_cells
.rho
.copy_advice(|| "rho", &mut region, col_l, 0)?;
copy(&mut region, || "e_1", col_m, 0, &gate_cells.e_1)?;
copy(&mut region, || "g_0", col_m, 1, &g_0)?;
gate_cells
.e_1
.copy_advice(|| "e_1", &mut region, col_m, 0)?;
g_0.copy_advice(|| "g_0", &mut region, col_m, 1)?;
copy(&mut region, || "f", col_r, 0, &gate_cells.f)?;
copy(
&mut region,
|| "e1_f_prime",
col_r,
1,
&gate_cells.e1_f_prime,
)?;
gate_cells.f.copy_advice(|| "f", &mut region, col_r, 0)?;
gate_cells
.e1_f_prime
.copy_advice(|| "e1_f_prime", &mut region, col_r, 1)?;
copy(&mut region, || "z13_f", col_z, 0, &gate_cells.z13_f)?;
copy(
&mut region,
gate_cells
.z13_f
.copy_advice(|| "z13_f", &mut region, col_z, 0)?;
gate_cells.z14_e1_f_prime.copy_advice(
|| "z14_e1_f_prime",
&mut region,
col_z,
1,
&gate_cells.z14_e1_f_prime,
)?;
self.q_notecommit_rho.enable(&mut region, 0)
@ -1352,28 +1370,33 @@ impl NoteCommitConfig {
layouter.assign_region(
|| "NoteCommit input psi",
|mut region| {
copy(&mut region, || "psi", col_l, 0, &gate_cells.psi)?;
copy(&mut region, || "h_0", col_l, 1, &gate_cells.h_0)?;
gate_cells
.psi
.copy_advice(|| "psi", &mut region, col_l, 0)?;
gate_cells
.h_0
.copy_advice(|| "h_0", &mut region, col_l, 1)?;
copy(&mut region, || "g_1", col_m, 0, &gate_cells.g_1)?;
copy(&mut region, || "h_1", col_m, 1, &h_1)?;
gate_cells
.g_1
.copy_advice(|| "g_1", &mut region, col_m, 0)?;
h_1.copy_advice(|| "h_1", &mut region, col_m, 1)?;
copy(&mut region, || "g_2 = z1_g", col_r, 0, &gate_cells.z1_g)?;
copy(
&mut region,
|| "g1_g2_prime",
col_r,
1,
&gate_cells.g1_g2_prime,
)?;
gate_cells
.z1_g
.copy_advice(|| "g_2 = z1_g", &mut region, col_r, 0)?;
gate_cells
.g1_g2_prime
.copy_advice(|| "g1_g2_prime", &mut region, col_r, 1)?;
copy(&mut region, || "z13_g", col_z, 0, &gate_cells.z13_g)?;
copy(
&mut region,
gate_cells
.z13_g
.copy_advice(|| "z13_g", &mut region, col_z, 0)?;
gate_cells.z13_g1_g2_prime.copy_advice(
|| "z13_g1_g2_prime",
&mut region,
col_z,
1,
&gate_cells.z13_g1_g2_prime,
)?;
self.q_notecommit_psi.enable(&mut region, 0)
@ -1383,46 +1406,46 @@ impl NoteCommitConfig {
}
struct GateCells {
a: CellValue<pallas::Base>,
b: CellValue<pallas::Base>,
b_0: CellValue<pallas::Base>,
a: AssignedCell<pallas::Base, pallas::Base>,
b: AssignedCell<pallas::Base, pallas::Base>,
b_0: AssignedCell<pallas::Base, pallas::Base>,
b_1: Option<pallas::Base>,
b_2: CellValue<pallas::Base>,
b_3: CellValue<pallas::Base>,
c: CellValue<pallas::Base>,
d: CellValue<pallas::Base>,
b_2: AssignedCell<pallas::Base, pallas::Base>,
b_3: AssignedCell<pallas::Base, pallas::Base>,
c: AssignedCell<pallas::Base, pallas::Base>,
d: AssignedCell<pallas::Base, pallas::Base>,
d_0: Option<pallas::Base>,
d_1: CellValue<pallas::Base>,
d_2: CellValue<pallas::Base>,
z1_d: CellValue<pallas::Base>,
e: CellValue<pallas::Base>,
e_0: CellValue<pallas::Base>,
e_1: CellValue<pallas::Base>,
f: CellValue<pallas::Base>,
g: CellValue<pallas::Base>,
d_1: AssignedCell<pallas::Base, pallas::Base>,
d_2: AssignedCell<pallas::Base, pallas::Base>,
z1_d: AssignedCell<pallas::Base, pallas::Base>,
e: AssignedCell<pallas::Base, pallas::Base>,
e_0: AssignedCell<pallas::Base, pallas::Base>,
e_1: AssignedCell<pallas::Base, pallas::Base>,
f: AssignedCell<pallas::Base, pallas::Base>,
g: AssignedCell<pallas::Base, pallas::Base>,
g_0: Option<pallas::Base>,
g_1: CellValue<pallas::Base>,
z1_g: CellValue<pallas::Base>,
h: CellValue<pallas::Base>,
h_0: CellValue<pallas::Base>,
g_1: AssignedCell<pallas::Base, pallas::Base>,
z1_g: AssignedCell<pallas::Base, pallas::Base>,
h: AssignedCell<pallas::Base, pallas::Base>,
h_0: AssignedCell<pallas::Base, pallas::Base>,
h_1: Option<pallas::Base>,
gd_x: CellValue<pallas::Base>,
pkd_x: CellValue<pallas::Base>,
value: CellValue<pallas::Base>,
rho: CellValue<pallas::Base>,
psi: CellValue<pallas::Base>,
a_prime: CellValue<pallas::Base>,
b3_c_prime: CellValue<pallas::Base>,
e1_f_prime: CellValue<pallas::Base>,
g1_g2_prime: CellValue<pallas::Base>,
z13_a_prime: CellValue<pallas::Base>,
z14_b3_c_prime: CellValue<pallas::Base>,
z14_e1_f_prime: CellValue<pallas::Base>,
z13_g1_g2_prime: CellValue<pallas::Base>,
z13_a: CellValue<pallas::Base>,
z13_c: CellValue<pallas::Base>,
z13_f: CellValue<pallas::Base>,
z13_g: CellValue<pallas::Base>,
gd_x: AssignedCell<pallas::Base, pallas::Base>,
pkd_x: AssignedCell<pallas::Base, pallas::Base>,
value: AssignedCell<pallas::Base, pallas::Base>,
rho: AssignedCell<pallas::Base, pallas::Base>,
psi: AssignedCell<pallas::Base, pallas::Base>,
a_prime: AssignedCell<pallas::Base, pallas::Base>,
b3_c_prime: AssignedCell<pallas::Base, pallas::Base>,
e1_f_prime: AssignedCell<pallas::Base, pallas::Base>,
g1_g2_prime: AssignedCell<pallas::Base, pallas::Base>,
z13_a_prime: AssignedCell<pallas::Base, pallas::Base>,
z14_b3_c_prime: AssignedCell<pallas::Base, pallas::Base>,
z14_e1_f_prime: AssignedCell<pallas::Base, pallas::Base>,
z13_g1_g2_prime: AssignedCell<pallas::Base, pallas::Base>,
z13_a: AssignedCell<pallas::Base, pallas::Base>,
z13_c: AssignedCell<pallas::Base, pallas::Base>,
z13_f: AssignedCell<pallas::Base, pallas::Base>,
z13_g: AssignedCell<pallas::Base, pallas::Base>,
}
#[cfg(test)]
@ -1435,9 +1458,7 @@ mod tests {
NonIdentityPoint,
},
sinsemilla::chip::SinsemillaChip,
utilities::{
lookup_range_check::LookupRangeCheckConfig, CellValue, UtilitiesInstructions,
},
utilities::{lookup_range_check::LookupRangeCheckConfig, UtilitiesInstructions},
},
constants::{L_ORCHARD_BASE, L_VALUE, NOTE_COMMITMENT_PERSONALIZATION, T_Q},
primitives::sinsemilla::CommitDomain,
@ -1446,7 +1467,7 @@ mod tests {
use ff::{Field, PrimeField, PrimeFieldBits};
use group::Curve;
use halo2::{
circuit::{Layouter, SimpleFloorPlanner},
circuit::{AssignedCell, Layouter, SimpleFloorPlanner},
dev::MockProver,
plonk::{Circuit, ConstraintSystem, Error},
};
@ -1471,7 +1492,7 @@ mod tests {
}
impl UtilitiesInstructions<pallas::Base> for MyCircuit {
type Var = CellValue<pallas::Base>;
type Var = AssignedCell<pallas::Base, pallas::Base>;
}
impl Circuit<pallas::Base> for MyCircuit {

View File

@ -2,7 +2,7 @@
use ff::PrimeFieldBits;
use halo2::{
circuit::{Cell, Layouter, Region},
circuit::{AssignedCell, Cell, Layouter},
plonk::{Advice, Column, Error, Expression},
};
use pasta_curves::arithmetic::FieldExt;
@ -12,18 +12,8 @@ pub(crate) mod cond_swap;
pub(crate) mod decompose_running_sum;
pub(crate) mod lookup_range_check;
/// A variable representing a field element.
#[derive(Copy, Clone, Debug)]
pub struct CellValue<F: FieldExt> {
cell: Cell,
value: Option<F>,
}
/// Trait for a variable in the circuit.
pub trait Var<F: FieldExt>: Copy + Clone + std::fmt::Debug {
/// Construct a new variable.
fn new(cell: Cell, value: Option<F>) -> Self;
pub trait Var<F: FieldExt>: Clone + std::fmt::Debug + From<AssignedCell<F, F>> {
/// The cell at which this variable was allocated.
fn cell(&self) -> Cell;
@ -31,17 +21,13 @@ pub trait Var<F: FieldExt>: Copy + Clone + std::fmt::Debug {
fn value(&self) -> Option<F>;
}
impl<F: FieldExt> Var<F> for CellValue<F> {
fn new(cell: Cell, value: Option<F>) -> Self {
Self { cell, value }
}
impl<F: FieldExt> Var<F> for AssignedCell<F, F> {
fn cell(&self) -> Cell {
self.cell
self.cell()
}
fn value(&self) -> Option<F> {
self.value
self.value().cloned()
}
}
@ -60,45 +46,19 @@ pub trait UtilitiesInstructions<F: FieldExt> {
layouter.assign_region(
|| "load private",
|mut region| {
let cell = region.assign_advice(
|| "load private",
column,
0,
|| value.ok_or(Error::Synthesis),
)?;
Ok(Var::new(cell, value))
region
.assign_advice(
|| "load private",
column,
0,
|| value.ok_or(Error::Synthesis),
)
.map(Self::Var::from)
},
)
}
}
/// Assigns a cell at a specific offset within the given region, constraining it
/// to the same value as another cell (which may be in any region).
///
/// Returns an error if either `column` or `copy` is not in a column that was passed to
/// [`ConstraintSystem::enable_equality`] during circuit configuration.
///
/// [`ConstraintSystem::enable_equality`]: halo2::plonk::ConstraintSystem::enable_equality
pub fn copy<A, AR, F: FieldExt>(
region: &mut Region<'_, F>,
annotation: A,
column: Column<Advice>,
offset: usize,
copy: &CellValue<F>,
) -> Result<CellValue<F>, Error>
where
A: Fn() -> AR,
AR: Into<String>,
{
let cell = region.assign_advice(annotation, column, offset, || {
copy.value.ok_or(Error::Synthesis)
})?;
region.constrain_equal(cell, copy.cell)?;
Ok(CellValue::new(cell, copy.value))
}
pub(crate) fn transpose_option_array<T: Copy + std::fmt::Debug, const LEN: usize>(
option_array: Option<[T; LEN]>,
) -> [Option<T>; LEN] {
@ -126,7 +86,7 @@ pub fn ternary<F: FieldExt>(a: Expression<F>, b: Expression<F>, c: Expression<F>
/// Takes a specified subsequence of the little-endian bit representation of a field element.
/// The bits are numbered from 0 for the LSB.
pub fn bitrange_subset<F: FieldExt + PrimeFieldBits>(field_elem: F, bitrange: Range<usize>) -> F {
pub fn bitrange_subset<F: FieldExt + PrimeFieldBits>(field_elem: &F, bitrange: Range<usize>) -> F {
assert!(bitrange.end <= F::NUM_BITS as usize);
let bits: Vec<bool> = field_elem
@ -172,7 +132,7 @@ mod tests {
struct MyCircuit<const RANGE: usize>(u8);
impl<const RANGE: usize> UtilitiesInstructions<pallas::Base> for MyCircuit<RANGE> {
type Var = CellValue<pallas::Base>;
type Var = AssignedCell<pallas::Base, pallas::Base>;
}
#[derive(Clone)]
@ -251,7 +211,7 @@ mod tests {
{
let field_elem = pallas::Base::rand();
let bitrange = 0..(pallas::Base::NUM_BITS as usize);
let subset = bitrange_subset(field_elem, bitrange);
let subset = bitrange_subset(&field_elem, bitrange);
assert_eq!(field_elem, subset);
}
@ -259,7 +219,7 @@ mod tests {
{
let field_elem = pallas::Base::rand();
let bitrange = 0..0;
let subset = bitrange_subset(field_elem, bitrange);
let subset = bitrange_subset(&field_elem, bitrange);
assert_eq!(pallas::Base::zero(), subset);
}
@ -286,7 +246,7 @@ mod tests {
let subsets = ranges
.iter()
.map(|range| bitrange_subset(field_elem, range.clone()))
.map(|range| bitrange_subset(&field_elem, range.clone()))
.collect::<Vec<_>>();
let mut sum = subsets[0];

View File

@ -1,6 +1,6 @@
use super::{bool_check, copy, ternary, CellValue, UtilitiesInstructions, Var};
use super::{bool_check, ternary, UtilitiesInstructions};
use halo2::{
circuit::{Chip, Layouter},
circuit::{AssignedCell, Chip, Layouter},
plonk::{Advice, Column, ConstraintSystem, Error, Selector},
poly::Rotation,
};
@ -53,7 +53,7 @@ pub struct CondSwapConfig {
}
impl<F: FieldExt> UtilitiesInstructions<F> for CondSwapChip<F> {
type Var = CellValue<F>;
type Var = AssignedCell<F, F>;
}
impl<F: FieldExt> CondSwapInstructions<F> for CondSwapChip<F> {
@ -73,18 +73,15 @@ impl<F: FieldExt> CondSwapInstructions<F> for CondSwapChip<F> {
config.q_swap.enable(&mut region, 0)?;
// Copy in `a` value
let a = copy(&mut region, || "copy a", config.a, 0, &pair.0)?;
let a = pair.0.copy_advice(|| "copy a", &mut region, config.a, 0)?;
// Witness `b` value
let b = {
let cell = region.assign_advice(
|| "witness b",
config.b,
0,
|| pair.1.ok_or(Error::Synthesis),
)?;
CellValue::new(cell, pair.1)
};
let b = region.assign_advice(
|| "witness b",
config.b,
0,
|| pair.1.ok_or(Error::Synthesis),
)?;
// Witness `swap` value
let swap_val = swap.map(|swap| F::from_u64(swap as u64));
@ -98,39 +95,33 @@ impl<F: FieldExt> CondSwapInstructions<F> for CondSwapChip<F> {
// Conditionally swap a
let a_swapped = {
let a_swapped = a
.value
.zip(b.value)
.value()
.zip(b.value())
.zip(swap)
.map(|((a, b), swap)| if swap { b } else { a });
let a_swapped_cell = region.assign_advice(
.map(|((a, b), swap)| if swap { b } else { a })
.cloned();
region.assign_advice(
|| "a_swapped",
config.a_swapped,
0,
|| a_swapped.ok_or(Error::Synthesis),
)?;
CellValue {
cell: a_swapped_cell,
value: a_swapped,
}
)?
};
// Conditionally swap b
let b_swapped = {
let b_swapped = a
.value
.zip(b.value)
.value()
.zip(b.value())
.zip(swap)
.map(|((a, b), swap)| if swap { a } else { b });
let b_swapped_cell = region.assign_advice(
.map(|((a, b), swap)| if swap { a } else { b })
.cloned();
region.assign_advice(
|| "b_swapped",
config.b_swapped,
0,
|| b_swapped.ok_or(Error::Synthesis),
)?;
CellValue {
cell: b_swapped_cell,
value: b_swapped,
}
)?
};
// Return swapped pair
@ -252,18 +243,21 @@ mod tests {
// Load the pair and the swap flag into the circuit.
let a = chip.load_private(layouter.namespace(|| "a"), config.a, self.a)?;
// Return the swapped pair.
let swapped_pair =
chip.swap(layouter.namespace(|| "swap"), (a, self.b), self.swap)?;
let swapped_pair = chip.swap(
layouter.namespace(|| "swap"),
(a.clone(), self.b),
self.swap,
)?;
if let Some(swap) = self.swap {
if swap {
// Check that `a` and `b` have been swapped
assert_eq!(swapped_pair.0.value.unwrap(), self.b.unwrap());
assert_eq!(swapped_pair.1.value.unwrap(), a.value.unwrap());
assert_eq!(swapped_pair.0.value().unwrap(), &self.b.unwrap());
assert_eq!(swapped_pair.1.value().unwrap(), a.value().unwrap());
} else {
// Check that `a` and `b` have not been swapped
assert_eq!(swapped_pair.0.value.unwrap(), a.value.unwrap());
assert_eq!(swapped_pair.1.value.unwrap(), self.b.unwrap());
assert_eq!(swapped_pair.0.value().unwrap(), a.value().unwrap());
assert_eq!(swapped_pair.1.value().unwrap(), &self.b.unwrap());
}
}

View File

@ -24,22 +24,22 @@
use ff::PrimeFieldBits;
use halo2::{
circuit::Region,
circuit::{AssignedCell, Region},
plonk::{Advice, Column, ConstraintSystem, Error, Selector},
poly::Rotation,
};
use super::{copy, range_check, CellValue, Var};
use super::range_check;
use crate::constants::util::decompose_word;
use pasta_curves::arithmetic::FieldExt;
use std::marker::PhantomData;
/// The running sum $[z_0, ..., z_W]$. If created in strict mode, $z_W = 0$.
pub struct RunningSum<F: FieldExt + PrimeFieldBits>(Vec<CellValue<F>>);
pub struct RunningSum<F: FieldExt + PrimeFieldBits>(Vec<AssignedCell<F, F>>);
impl<F: FieldExt + PrimeFieldBits> std::ops::Deref for RunningSum<F> {
type Target = Vec<CellValue<F>>;
type Target = Vec<AssignedCell<F, F>>;
fn deref(&self) -> &Vec<CellValue<F>> {
fn deref(&self) -> &Vec<AssignedCell<F, F>> {
&self.0
}
}
@ -105,15 +105,12 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
word_num_bits: usize,
num_windows: usize,
) -> Result<RunningSum<F>, Error> {
let z_0 = {
let cell = region.assign_advice(
|| "z_0 = alpha",
self.z,
offset,
|| alpha.ok_or(Error::Synthesis),
)?;
CellValue::new(cell, alpha)
};
let z_0 = region.assign_advice(
|| "z_0 = alpha",
self.z,
offset,
|| alpha.ok_or(Error::Synthesis),
)?;
self.decompose(region, offset, z_0, strict, word_num_bits, num_windows)
}
@ -125,12 +122,12 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
&self,
region: &mut Region<'_, F>,
offset: usize,
alpha: CellValue<F>,
alpha: AssignedCell<F, F>,
strict: bool,
word_num_bits: usize,
num_windows: usize,
) -> Result<RunningSum<F>, Error> {
let z_0 = copy(region, || "copy z_0 = alpha", self.z, offset, &alpha)?;
let z_0 = alpha.copy_advice(|| "copy z_0 = alpha", region, self.z, offset)?;
self.decompose(region, offset, z_0, strict, word_num_bits, num_windows)
}
@ -143,7 +140,7 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
&self,
region: &mut Region<'_, F>,
offset: usize,
z_0: CellValue<F>,
z_0: AssignedCell<F, F>,
strict: bool,
word_num_bits: usize,
num_windows: usize,
@ -179,7 +176,7 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
};
// Initialize empty vector to store running sum values [z_0, ..., z_W].
let mut zs: Vec<CellValue<F>> = vec![z_0];
let mut zs: Vec<AssignedCell<F, F>> = vec![z_0.clone()];
let mut z = z_0;
// Assign running sum `z_{i+1}` = (z_i - k_i) / (2^K) for i = 0..=n-1.
@ -193,19 +190,18 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
let z_next_val = z
.value()
.zip(word)
.map(|(z_cur_val, word)| (z_cur_val - word) * two_pow_k_inv);
let cell = region.assign_advice(
.map(|(z_cur_val, word)| (*z_cur_val - word) * two_pow_k_inv);
region.assign_advice(
|| format!("z_{:?}", i + 1),
self.z,
offset + i + 1,
|| z_next_val.ok_or(Error::Synthesis),
)?;
CellValue::new(cell, z_next_val)
)?
};
// Update `z`.
z = z_next;
zs.push(z);
zs.push(z.clone());
}
assert_eq!(zs.len(), num_windows + 1);
@ -284,7 +280,7 @@ mod tests {
WORD_NUM_BITS,
NUM_WINDOWS,
)?;
let alpha = zs[0];
let alpha = zs[0].clone();
let offset = offset + NUM_WINDOWS + 1;

View File

@ -3,7 +3,7 @@
use crate::spec::lebs2ip;
use halo2::{
circuit::{Layouter, Region},
circuit::{AssignedCell, Layouter, Region},
plonk::{Advice, Column, ConstraintSystem, Error, Selector, TableColumn},
poly::Rotation,
};
@ -14,11 +14,11 @@ use ff::PrimeFieldBits;
use super::*;
/// The running sum $[z_0, ..., z_W]$. If created in strict mode, $z_W = 0$.
pub struct RunningSum<F: FieldExt + PrimeFieldBits>(Vec<CellValue<F>>);
pub struct RunningSum<F: FieldExt + PrimeFieldBits>(Vec<AssignedCell<F, F>>);
impl<F: FieldExt + PrimeFieldBits> std::ops::Deref for RunningSum<F> {
type Target = Vec<CellValue<F>>;
type Target = Vec<AssignedCell<F, F>>;
fn deref(&self) -> &Vec<CellValue<F>> {
fn deref(&self) -> &Vec<AssignedCell<F, F>> {
&self.0
}
}
@ -143,7 +143,7 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
pub fn copy_check(
&self,
mut layouter: impl Layouter<F>,
element: CellValue<F>,
element: AssignedCell<F, F>,
num_words: usize,
strict: bool,
) -> Result<RunningSum<F>, Error> {
@ -151,7 +151,7 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
|| format!("{:?} words range check", num_words),
|mut region| {
// Copy `element` and initialize running sum `z_0 = element` to decompose it.
let z_0 = copy(&mut region, || "z_0", self.running_sum, 0, &element)?;
let z_0 = element.copy_advice(|| "z_0", &mut region, self.running_sum, 0)?;
self.range_check(&mut region, z_0, num_words, strict)
},
)
@ -168,15 +168,12 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
layouter.assign_region(
|| "Witness element",
|mut region| {
let z_0 = {
let cell = region.assign_advice(
|| "Witness element",
self.running_sum,
0,
|| value.ok_or(Error::Synthesis),
)?;
CellValue::new(cell, value)
};
let z_0 = region.assign_advice(
|| "Witness element",
self.running_sum,
0,
|| value.ok_or(Error::Synthesis),
)?;
self.range_check(&mut region, z_0, num_words, strict)
},
)
@ -192,7 +189,7 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
fn range_check(
&self,
region: &mut Region<'_, F>,
element: CellValue<F>,
element: AssignedCell<F, F>,
num_words: usize,
strict: bool,
) -> Result<RunningSum<F>, Error> {
@ -223,7 +220,7 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
}
};
let mut zs = vec![element];
let mut zs = vec![element.clone()];
// Assign cumulative sum such that
// z_i = 2^{K}⋅z_{i + 1} + a_i
@ -244,19 +241,17 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
let z_val = z
.value()
.zip(*word)
.map(|(z, word)| (z - word) * inv_two_pow_k);
.map(|(z, word)| (*z - word) * inv_two_pow_k);
// Assign z_next
let z_cell = region.assign_advice(
region.assign_advice(
|| format!("z_{:?}", idx + 1),
self.running_sum,
idx + 1,
|| z_val.ok_or(Error::Synthesis),
)?;
CellValue::new(z_cell, z_val)
)?
};
zs.push(z);
zs.push(z.clone());
}
if strict {
@ -275,7 +270,7 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
pub fn copy_short_check(
&self,
mut layouter: impl Layouter<F>,
element: CellValue<F>,
element: AssignedCell<F, F>,
num_bits: usize,
) -> Result<(), Error> {
assert!(num_bits < K);
@ -283,7 +278,8 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
|| format!("Range check {:?} bits", num_bits),
|mut region| {
// Copy `element` to use in the k-bit lookup.
let element = copy(&mut region, || "element", self.running_sum, 0, &element)?;
let element =
element.copy_advice(|| "element", &mut region, self.running_sum, 0)?;
self.short_range_check(&mut region, element, num_bits)
},
@ -300,23 +296,20 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
mut layouter: impl Layouter<F>,
element: Option<F>,
num_bits: usize,
) -> Result<CellValue<F>, Error> {
) -> Result<AssignedCell<F, F>, Error> {
assert!(num_bits <= K);
layouter.assign_region(
|| format!("Range check {:?} bits", num_bits),
|mut region| {
// Witness `element` to use in the k-bit lookup.
let element = {
let cell = region.assign_advice(
|| "Witness element",
self.running_sum,
0,
|| element.ok_or(Error::Synthesis),
)?;
CellValue::new(cell, element)
};
let element = region.assign_advice(
|| "Witness element",
self.running_sum,
0,
|| element.ok_or(Error::Synthesis),
)?;
self.short_range_check(&mut region, element, num_bits)?;
self.short_range_check(&mut region, element.clone(), num_bits)?;
Ok(element)
},
@ -329,7 +322,7 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
fn short_range_check(
&self,
region: &mut Region<'_, F>,
element: CellValue<F>,
element: AssignedCell<F, F>,
num_bits: usize,
) -> Result<(), Error> {
// Enable lookup for `element`, to constrain it to 10 bits.
@ -344,7 +337,7 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
// Assign shifted `element * 2^{K - num_bits}`
let shifted = element.value().map(|element| {
let shift = F::from_u64(1 << (K - num_bits));
element * shift
*element * shift
});
region.assign_advice(
@ -369,7 +362,6 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
#[cfg(test)]
mod tests {
use super::super::Var;
use super::LookupRangeCheckConfig;
use crate::primitives::sinsemilla::{INV_TWO_POW_K, K};
@ -468,7 +460,7 @@ mod tests {
for (expected_z, z) in expected_zs.into_iter().zip(zs.iter()) {
if let Some(z) = z.value() {
assert_eq!(expected_z, z);
assert_eq!(&expected_z, z);
}
}
}

View File

@ -10,7 +10,7 @@ use halo2::arithmetic::{CurveAffine, FieldExt};
/// We are returning a `Vec<u8>` which means the window size is limited to
/// <= 8 bits.
pub fn decompose_word<F: PrimeFieldBits>(
word: F,
word: &F,
word_num_bits: usize,
window_num_bits: usize,
) -> Vec<u8> {
@ -86,7 +86,7 @@ mod tests {
window_num_bits in 1u8..9
) {
// Get decomposition into `window_num_bits` bits
let decomposed = decompose_word(scalar, pallas::Scalar::NUM_BITS as usize, window_num_bits as usize);
let decomposed = decompose_word(&scalar, pallas::Scalar::NUM_BITS as usize, window_num_bits as usize);
// Flatten bits
let bits = decomposed

View File

@ -1,6 +1,7 @@
//! The Poseidon algebraic hash function.
use std::array;
use std::convert::TryInto;
use std::fmt;
use std::iter;
use std::marker::PhantomData;
@ -147,9 +148,13 @@ pub(crate) enum Sponge<F, const RATE: usize> {
Squeezing(SpongeState<F, RATE>),
}
impl<F: Copy, const RATE: usize> Sponge<F, RATE> {
impl<F: fmt::Debug, const RATE: usize> Sponge<F, RATE> {
pub(crate) fn absorb(val: F) -> Self {
let mut input = [None; RATE];
let mut input: [Option<F>; RATE] = (0..RATE)
.map(|_| None)
.collect::<Vec<_>>()
.try_into()
.unwrap();
input[0] = Some(val);
Sponge::Absorbing(input)
}