mirror of
https://github.com/AtHeartEngineer/halo2.git
synced 2026-01-09 20:48:02 -05:00
Merge pull request #598 from zcash/circuit-value-type
Introduce `halo2_proofs::circuit::Value`
This commit is contained in:
@@ -6,6 +6,48 @@ and this project adheres to Rust's notion of
|
||||
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- `halo2_gadgets::utilities::RangeConstrained<F, Value<F>>::bitrange_of`
|
||||
|
||||
### Changed
|
||||
All APIs that represented witnessed values as `Option<V>` now represent them as
|
||||
`halo2_proofs::circuit::Value<V>`. The core API changes are listed below.
|
||||
|
||||
- The following APIs now take `Value<_>` instead of `Option<_>`:
|
||||
- `halo2_gadgets::ecc`:
|
||||
- `EccInstructions::{witness_point, witness_point_non_id}`
|
||||
- `EccInstructions::{witness_scalar_var, witness_scalar_fixed}`
|
||||
- `ScalarVar::new`
|
||||
- `ScalarFixed::new`
|
||||
- `NonIdentityPoint::new`
|
||||
- `Point::new`
|
||||
- `halo2_gadgets::sinsemilla`:
|
||||
- `SinsemillaInstructions::witness_message_piece`
|
||||
- `MessagePiece::{from_field_elem, from_subpieces}`
|
||||
- `halo2_gadgets::sinsemilla::merkle`:
|
||||
- `MerklePath::construct`
|
||||
- `halo2_gadgets::utilities`:
|
||||
- `UtilitiesInstructions::load_private`
|
||||
- `RangeConstrained::witness_short`
|
||||
- `halo2_gadgets::utilities::cond_swap`:
|
||||
- `CondSwapInstructions::swap`
|
||||
- `halo2_gadgets::utilities::decompose_running_sum`:
|
||||
- `RunningSumConfig::witness_decompose`
|
||||
- `halo2_gadgets::utilities::lookup_range_check`:
|
||||
- `LookupRangeCheckConfig::{witness_check, witness_short_check}`
|
||||
- The following APIs now return `Value<_>` instead of `Option<_>`:
|
||||
- `halo2_gadgets::ecc::chip`:
|
||||
- `EccPoint::{point, is_identity}`
|
||||
- `NonIdentityEccPoint::point`
|
||||
- `halo2_gadgets::utilities`:
|
||||
- `FieldValue::value`
|
||||
- `Var::value`
|
||||
- `RangeConstrained::value`
|
||||
- `halo2_gadgets::sha256::BlockWord` is now a newtype wrapper around
|
||||
`Value<u32>` instead of `Option<u32>`.
|
||||
|
||||
### Removed
|
||||
- `halo2_gadgets::utilities::RangeConstrained<F, Option<F>>::bitrange_of`
|
||||
|
||||
## [0.1.0] - 2022-05-10
|
||||
### Added
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use ff::Field;
|
||||
use halo2_proofs::{
|
||||
circuit::{Layouter, SimpleFloorPlanner},
|
||||
circuit::{Layouter, SimpleFloorPlanner, Value},
|
||||
pasta::Fp,
|
||||
plonk::{
|
||||
create_proof, keygen_pk, keygen_vk, verify_proof, Advice, Circuit, Column,
|
||||
@@ -26,10 +26,10 @@ struct HashCircuit<S, const WIDTH: usize, const RATE: usize, const L: usize>
|
||||
where
|
||||
S: Spec<Fp, WIDTH, RATE> + Clone + Copy,
|
||||
{
|
||||
message: Option<[Fp; L]>,
|
||||
message: Value<[Fp; L]>,
|
||||
// For the purpose of this test, witness the result.
|
||||
// TODO: Move this into an instance column.
|
||||
output: Option<Fp>,
|
||||
output: Value<Fp>,
|
||||
_spec: PhantomData<S>,
|
||||
}
|
||||
|
||||
@@ -49,8 +49,8 @@ where
|
||||
|
||||
fn without_witnesses(&self) -> Self {
|
||||
Self {
|
||||
message: None,
|
||||
output: None,
|
||||
message: Value::unknown(),
|
||||
output: Value::unknown(),
|
||||
_spec: PhantomData,
|
||||
}
|
||||
}
|
||||
@@ -92,7 +92,7 @@ where
|
||||
|| format!("load message_{}", i),
|
||||
config.input[i],
|
||||
0,
|
||||
|| value.ok_or(Error::Synthesis),
|
||||
|| value,
|
||||
)
|
||||
};
|
||||
|
||||
@@ -110,12 +110,8 @@ where
|
||||
layouter.assign_region(
|
||||
|| "constrain output",
|
||||
|mut region| {
|
||||
let expected_var = region.assign_advice(
|
||||
|| "load output",
|
||||
config.input[0],
|
||||
0,
|
||||
|| self.output.ok_or(Error::Synthesis),
|
||||
)?;
|
||||
let expected_var =
|
||||
region.assign_advice(|| "load output", config.input[0], 0, || self.output)?;
|
||||
region.constrain_equal(output.cell(), expected_var.cell())
|
||||
},
|
||||
)
|
||||
@@ -191,8 +187,8 @@ fn bench_poseidon<S, const WIDTH: usize, const RATE: usize, const L: usize>(
|
||||
let params: Params<vesta::Affine> = Params::new(K);
|
||||
|
||||
let empty_circuit = HashCircuit::<S, WIDTH, RATE, L> {
|
||||
message: None,
|
||||
output: None,
|
||||
message: Value::unknown(),
|
||||
output: Value::unknown(),
|
||||
_spec: PhantomData,
|
||||
};
|
||||
|
||||
@@ -212,8 +208,8 @@ fn bench_poseidon<S, const WIDTH: usize, const RATE: usize, const L: usize>(
|
||||
let output = poseidon::Hash::<_, S, ConstantLength<L>, WIDTH, RATE>::init().hash(message);
|
||||
|
||||
let circuit = HashCircuit::<S, WIDTH, RATE, L> {
|
||||
message: Some(message),
|
||||
output: Some(output),
|
||||
message: Value::known(message),
|
||||
output: Value::known(output),
|
||||
_spec: PhantomData,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use halo2_proofs::{
|
||||
circuit::{Layouter, SimpleFloorPlanner},
|
||||
circuit::{Layouter, SimpleFloorPlanner, Value},
|
||||
pasta::{pallas, EqAffine},
|
||||
plonk::{
|
||||
create_proof, keygen_pk, keygen_vk, verify_proof, Circuit, ConstraintSystem, Error,
|
||||
@@ -47,22 +47,22 @@ fn bench(name: &str, k: u32, c: &mut Criterion) {
|
||||
|
||||
// Test vector: "abc"
|
||||
let test_input = [
|
||||
BlockWord(Some(0b01100001011000100110001110000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000011000)),
|
||||
BlockWord(Value::known(0b01100001011000100110001110000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000011000)),
|
||||
];
|
||||
|
||||
// Create a message of length 31 blocks
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::fmt::Debug;
|
||||
|
||||
use halo2_proofs::{
|
||||
arithmetic::CurveAffine,
|
||||
circuit::{Chip, Layouter},
|
||||
circuit::{Chip, Layouter, Value},
|
||||
plonk::Error,
|
||||
};
|
||||
|
||||
@@ -57,7 +57,7 @@ pub trait EccInstructions<C: CurveAffine>:
|
||||
fn witness_point(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<C::Base>,
|
||||
value: Option<C>,
|
||||
value: Value<C>,
|
||||
) -> Result<Self::Point, Error>;
|
||||
|
||||
/// Witnesses the given point as a private input to the circuit.
|
||||
@@ -65,21 +65,21 @@ pub trait EccInstructions<C: CurveAffine>:
|
||||
fn witness_point_non_id(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<C::Base>,
|
||||
value: Option<C>,
|
||||
value: Value<C>,
|
||||
) -> Result<Self::NonIdentityPoint, Error>;
|
||||
|
||||
/// Witnesses a full-width scalar to be used in variable-base multiplication.
|
||||
fn witness_scalar_var(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<C::Base>,
|
||||
value: Option<C::Scalar>,
|
||||
value: Value<C::Scalar>,
|
||||
) -> Result<Self::ScalarVar, Error>;
|
||||
|
||||
/// Witnesses a full-width scalar to be used in fixed-base multiplication.
|
||||
fn witness_scalar_fixed(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<C::Base>,
|
||||
value: Option<C::Scalar>,
|
||||
value: Value<C::Scalar>,
|
||||
) -> Result<Self::ScalarFixed, Error>;
|
||||
|
||||
/// Converts a magnitude and sign that exists as variables in the circuit into a
|
||||
@@ -184,7 +184,7 @@ impl<C: CurveAffine, EccChip: EccInstructions<C>> ScalarVar<C, EccChip> {
|
||||
pub fn new(
|
||||
chip: EccChip,
|
||||
mut layouter: impl Layouter<C::Base>,
|
||||
value: Option<C::Scalar>,
|
||||
value: Value<C::Scalar>,
|
||||
) -> Result<Self, Error> {
|
||||
let scalar = chip.witness_scalar_var(&mut layouter, value);
|
||||
scalar.map(|inner| ScalarVar { chip, inner })
|
||||
@@ -219,7 +219,7 @@ impl<C: CurveAffine, EccChip: EccInstructions<C>> ScalarFixed<C, EccChip> {
|
||||
pub fn new(
|
||||
chip: EccChip,
|
||||
mut layouter: impl Layouter<C::Base>,
|
||||
value: Option<C::Scalar>,
|
||||
value: Value<C::Scalar>,
|
||||
) -> Result<Self, Error> {
|
||||
let scalar = chip.witness_scalar_fixed(&mut layouter, value);
|
||||
scalar.map(|inner| ScalarFixed { chip, inner })
|
||||
@@ -266,7 +266,7 @@ impl<C: CurveAffine, EccChip: EccInstructions<C>> NonIdentityPoint<C, EccChip> {
|
||||
pub fn new(
|
||||
chip: EccChip,
|
||||
mut layouter: impl Layouter<C::Base>,
|
||||
value: Option<C>,
|
||||
value: Value<C>,
|
||||
) -> Result<Self, Error> {
|
||||
let point = chip.witness_point_non_id(&mut layouter, value);
|
||||
point.map(|inner| NonIdentityPoint { chip, inner })
|
||||
@@ -384,7 +384,7 @@ impl<C: CurveAffine, EccChip: EccInstructions<C> + Clone + Debug + Eq> Point<C,
|
||||
pub fn new(
|
||||
chip: EccChip,
|
||||
mut layouter: impl Layouter<C::Base>,
|
||||
value: Option<C>,
|
||||
value: Value<C>,
|
||||
) -> Result<Self, Error> {
|
||||
let point = chip.witness_point(&mut layouter, value);
|
||||
point.map(|inner| Point { chip, inner })
|
||||
@@ -581,7 +581,7 @@ pub(crate) mod tests {
|
||||
use group::{prime::PrimeCurveAffine, Curve, Group};
|
||||
|
||||
use halo2_proofs::{
|
||||
circuit::{Layouter, SimpleFloorPlanner},
|
||||
circuit::{Layouter, SimpleFloorPlanner, Value},
|
||||
dev::MockProver,
|
||||
plonk::{Circuit, ConstraintSystem, Error},
|
||||
};
|
||||
@@ -784,13 +784,13 @@ pub(crate) mod tests {
|
||||
let p = super::NonIdentityPoint::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "P"),
|
||||
Some(p_val),
|
||||
Value::known(p_val),
|
||||
)?;
|
||||
let p_neg = -p_val;
|
||||
let p_neg = super::NonIdentityPoint::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "-P"),
|
||||
Some(p_neg),
|
||||
Value::known(p_neg),
|
||||
)?;
|
||||
|
||||
// Generate a random non-identity point Q
|
||||
@@ -798,7 +798,7 @@ pub(crate) mod tests {
|
||||
let q = super::NonIdentityPoint::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "Q"),
|
||||
Some(q_val),
|
||||
Value::known(q_val),
|
||||
)?;
|
||||
|
||||
// Make sure P and Q are not the same point.
|
||||
@@ -809,13 +809,13 @@ pub(crate) mod tests {
|
||||
let _ = super::Point::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "identity"),
|
||||
Some(pallas::Affine::identity()),
|
||||
Value::known(pallas::Affine::identity()),
|
||||
)?;
|
||||
|
||||
super::NonIdentityPoint::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "identity"),
|
||||
Some(pallas::Affine::identity()),
|
||||
Value::known(pallas::Affine::identity()),
|
||||
)
|
||||
.expect_err("Trying to witness the identity should return an error");
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use arrayvec::ArrayVec;
|
||||
use ff::PrimeField;
|
||||
use group::prime::PrimeCurveAffine;
|
||||
use halo2_proofs::{
|
||||
circuit::{AssignedCell, Chip, Layouter},
|
||||
circuit::{AssignedCell, Chip, Layouter, Value},
|
||||
plonk::{Advice, Assigned, Column, ConstraintSystem, Error, Fixed},
|
||||
};
|
||||
use pasta_curves::{arithmetic::CurveAffine, pallas};
|
||||
@@ -57,17 +57,14 @@ impl EccPoint {
|
||||
}
|
||||
|
||||
/// Returns the value of this curve point, if known.
|
||||
pub fn point(&self) -> Option<pallas::Affine> {
|
||||
match (self.x.value(), self.y.value()) {
|
||||
(Some(x), Some(y)) => {
|
||||
if x.is_zero_vartime() && y.is_zero_vartime() {
|
||||
Some(pallas::Affine::identity())
|
||||
} else {
|
||||
Some(pallas::Affine::from_xy(x.evaluate(), y.evaluate()).unwrap())
|
||||
}
|
||||
pub fn point(&self) -> Value<pallas::Affine> {
|
||||
self.x.value().zip(self.y.value()).map(|(x, y)| {
|
||||
if x.is_zero_vartime() && y.is_zero_vartime() {
|
||||
pallas::Affine::identity()
|
||||
} else {
|
||||
pallas::Affine::from_xy(x.evaluate(), y.evaluate()).unwrap()
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
}
|
||||
/// The cell containing the affine short-Weierstrass x-coordinate,
|
||||
/// or 0 for the zero point.
|
||||
@@ -81,7 +78,7 @@ impl EccPoint {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn is_identity(&self) -> Option<bool> {
|
||||
fn is_identity(&self) -> Value<bool> {
|
||||
self.x.value().map(|x| x.is_zero_vartime())
|
||||
}
|
||||
}
|
||||
@@ -113,14 +110,11 @@ impl NonIdentityEccPoint {
|
||||
}
|
||||
|
||||
/// Returns the value of this curve point, if known.
|
||||
pub fn point(&self) -> Option<pallas::Affine> {
|
||||
match (self.x.value(), self.y.value()) {
|
||||
(Some(x), Some(y)) => {
|
||||
assert!(!x.is_zero_vartime() && !y.is_zero_vartime());
|
||||
Some(pallas::Affine::from_xy(x.evaluate(), y.evaluate()).unwrap())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
pub fn point(&self) -> Value<pallas::Affine> {
|
||||
self.x.value().zip(self.y.value()).map(|(x, y)| {
|
||||
assert!(!x.is_zero_vartime() && !y.is_zero_vartime());
|
||||
pallas::Affine::from_xy(x.evaluate(), y.evaluate()).unwrap()
|
||||
})
|
||||
}
|
||||
/// The cell containing the affine short-Weierstrass x-coordinate.
|
||||
pub fn x(&self) -> AssignedCell<pallas::Base, pallas::Base> {
|
||||
@@ -336,7 +330,7 @@ impl<FixedPoints: super::FixedPoints<pallas::Affine>> EccChip<FixedPoints> {
|
||||
/// each `k_i` is in the range [0..2^3).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EccScalarFixed {
|
||||
value: Option<pallas::Scalar>,
|
||||
value: Value<pallas::Scalar>,
|
||||
/// The circuit-assigned windows representing this scalar, or `None` if the scalar has
|
||||
/// not been used yet.
|
||||
windows: Option<ArrayVec<AssignedCell<pallas::Base, pallas::Base>, { NUM_WINDOWS }>>,
|
||||
@@ -450,7 +444,7 @@ where
|
||||
fn witness_point(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<pallas::Base>,
|
||||
value: Option<pallas::Affine>,
|
||||
value: Value<pallas::Affine>,
|
||||
) -> Result<Self::Point, Error> {
|
||||
let config = self.config().witness_point;
|
||||
layouter.assign_region(
|
||||
@@ -462,7 +456,7 @@ where
|
||||
fn witness_point_non_id(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<pallas::Base>,
|
||||
value: Option<pallas::Affine>,
|
||||
value: Value<pallas::Affine>,
|
||||
) -> Result<Self::NonIdentityPoint, Error> {
|
||||
let config = self.config().witness_point;
|
||||
layouter.assign_region(
|
||||
@@ -474,7 +468,7 @@ where
|
||||
fn witness_scalar_var(
|
||||
&self,
|
||||
_layouter: &mut impl Layouter<pallas::Base>,
|
||||
_value: Option<pallas::Scalar>,
|
||||
_value: Value<pallas::Scalar>,
|
||||
) -> Result<Self::ScalarVar, Error> {
|
||||
// This is unimplemented for halo2_gadgets v0.1.0.
|
||||
todo!()
|
||||
@@ -483,7 +477,7 @@ where
|
||||
fn witness_scalar_fixed(
|
||||
&self,
|
||||
_layouter: &mut impl Layouter<pallas::Base>,
|
||||
value: Option<pallas::Scalar>,
|
||||
value: Value<pallas::Scalar>,
|
||||
) -> Result<Self::ScalarFixed, Error> {
|
||||
Ok(EccScalarFixed {
|
||||
value,
|
||||
|
||||
@@ -227,16 +227,16 @@ impl Config {
|
||||
let (x_q, y_q) = (q.x.value(), q.y.value());
|
||||
|
||||
// Assign α = inv0(x_q - x_p)
|
||||
let alpha = x_p.zip(x_q).map(|(x_p, x_q)| (x_q - x_p).invert());
|
||||
region.assign_advice(|| "α", self.alpha, offset, || alpha.ok_or(Error::Synthesis))?;
|
||||
let alpha = (x_q - x_p).invert();
|
||||
region.assign_advice(|| "α", self.alpha, offset, || alpha)?;
|
||||
|
||||
// Assign β = inv0(x_p)
|
||||
let beta = x_p.map(|x_p| x_p.invert());
|
||||
region.assign_advice(|| "β", self.beta, offset, || beta.ok_or(Error::Synthesis))?;
|
||||
let beta = x_p.invert();
|
||||
region.assign_advice(|| "β", self.beta, offset, || beta)?;
|
||||
|
||||
// Assign γ = inv0(x_q)
|
||||
let gamma = x_q.map(|x_q| x_q.invert());
|
||||
region.assign_advice(|| "γ", self.gamma, offset, || gamma.ok_or(Error::Synthesis))?;
|
||||
let gamma = x_q.invert();
|
||||
region.assign_advice(|| "γ", self.gamma, offset, || gamma)?;
|
||||
|
||||
// Assign δ = inv0(y_q + y_p) if x_q = x_p, 0 otherwise
|
||||
let delta = x_p
|
||||
@@ -250,7 +250,7 @@ impl Config {
|
||||
Assigned::Zero
|
||||
}
|
||||
});
|
||||
region.assign_advice(|| "δ", self.delta, offset, || delta.ok_or(Error::Synthesis))?;
|
||||
region.assign_advice(|| "δ", self.delta, offset, || delta)?;
|
||||
|
||||
#[allow(clippy::collapsible_else_if)]
|
||||
// Assign lambda
|
||||
@@ -278,12 +278,7 @@ impl Config {
|
||||
}
|
||||
}
|
||||
});
|
||||
region.assign_advice(
|
||||
|| "λ",
|
||||
self.lambda,
|
||||
offset,
|
||||
|| lambda.ok_or(Error::Synthesis),
|
||||
)?;
|
||||
region.assign_advice(|| "λ", self.lambda, offset, || lambda)?;
|
||||
|
||||
// Calculate (x_r, y_r)
|
||||
let r =
|
||||
@@ -314,21 +309,11 @@ impl Config {
|
||||
|
||||
// Assign x_r
|
||||
let x_r = r.map(|r| r.0);
|
||||
let x_r_cell = region.assign_advice(
|
||||
|| "x_r",
|
||||
self.x_qr,
|
||||
offset + 1,
|
||||
|| x_r.ok_or(Error::Synthesis),
|
||||
)?;
|
||||
let x_r_cell = region.assign_advice(|| "x_r", self.x_qr, offset + 1, || x_r)?;
|
||||
|
||||
// Assign y_r
|
||||
let y_r = r.map(|r| r.1);
|
||||
let y_r_cell = region.assign_advice(
|
||||
|| "y_r",
|
||||
self.y_qr,
|
||||
offset + 1,
|
||||
|| y_r.ok_or(Error::Synthesis),
|
||||
)?;
|
||||
let y_r_cell = region.assign_advice(|| "y_r", self.y_qr, offset + 1, || y_r)?;
|
||||
|
||||
let result = EccPoint {
|
||||
x: x_r_cell,
|
||||
@@ -345,9 +330,9 @@ impl Config {
|
||||
let real_sum = p.zip(q).map(|(p, q)| p + q);
|
||||
let result = result.point();
|
||||
|
||||
if let (Some(real_sum), Some(result)) = (real_sum, result) {
|
||||
assert_eq!(real_sum.to_affine(), result);
|
||||
}
|
||||
real_sum
|
||||
.zip(result)
|
||||
.assert_if_known(|(real_sum, result)| &real_sum.to_affine() == result);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
@@ -357,7 +342,10 @@ impl Config {
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use group::{prime::PrimeCurveAffine, Curve};
|
||||
use halo2_proofs::{circuit::Layouter, plonk::Error};
|
||||
use halo2_proofs::{
|
||||
circuit::{Layouter, Value},
|
||||
plonk::Error,
|
||||
};
|
||||
use pasta_curves::{arithmetic::CurveExt, pallas};
|
||||
|
||||
use crate::ecc::{chip::EccPoint, EccInstructions, NonIdentityPoint};
|
||||
@@ -380,9 +368,10 @@ pub mod tests {
|
||||
// Check complete addition P + (-P)
|
||||
let zero = {
|
||||
let result = p.add(layouter.namespace(|| "P + (-P)"), p_neg)?;
|
||||
if let Some(is_identity) = result.inner().is_identity() {
|
||||
assert!(is_identity);
|
||||
}
|
||||
result
|
||||
.inner()
|
||||
.is_identity()
|
||||
.assert_if_known(|is_identity| *is_identity);
|
||||
result
|
||||
};
|
||||
|
||||
@@ -398,7 +387,7 @@ pub mod tests {
|
||||
let witnessed_result = NonIdentityPoint::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "witnessed P + Q"),
|
||||
Some((p_val + q_val).to_affine()),
|
||||
Value::known((p_val + q_val).to_affine()),
|
||||
)?;
|
||||
result.constrain_equal(layouter.namespace(|| "constrain P + Q"), &witnessed_result)?;
|
||||
}
|
||||
@@ -409,7 +398,7 @@ pub mod tests {
|
||||
let witnessed_result = NonIdentityPoint::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "witnessed P + P"),
|
||||
Some((p_val + p_val).to_affine()),
|
||||
Value::known((p_val + p_val).to_affine()),
|
||||
)?;
|
||||
result.constrain_equal(layouter.namespace(|| "constrain P + P"), &witnessed_result)?;
|
||||
}
|
||||
@@ -431,7 +420,7 @@ pub mod tests {
|
||||
let endo_p = NonIdentityPoint::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "endo(P)"),
|
||||
Some(endo_p.to_affine()),
|
||||
Value::known(endo_p.to_affine()),
|
||||
)?;
|
||||
p.add(layouter.namespace(|| "P + endo(P)"), &endo_p)?;
|
||||
|
||||
@@ -440,7 +429,7 @@ pub mod tests {
|
||||
let endo_p_neg = NonIdentityPoint::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "endo(-P)"),
|
||||
Some(endo_p_neg.to_affine()),
|
||||
Value::known(endo_p_neg.to_affine()),
|
||||
)?;
|
||||
p.add(layouter.namespace(|| "P + endo(-P)"), &endo_p_neg)?;
|
||||
|
||||
@@ -449,7 +438,7 @@ pub mod tests {
|
||||
let endo_2_p = NonIdentityPoint::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "endo^2(P)"),
|
||||
Some(endo_2_p.to_affine()),
|
||||
Value::known(endo_2_p.to_affine()),
|
||||
)?;
|
||||
p.add(layouter.namespace(|| "P + endo^2(P)"), &endo_2_p)?;
|
||||
|
||||
@@ -458,7 +447,7 @@ pub mod tests {
|
||||
let endo_2_p_neg = NonIdentityPoint::new(
|
||||
chip,
|
||||
layouter.namespace(|| "endo^2(-P)"),
|
||||
Some(endo_2_p_neg.to_affine()),
|
||||
Value::known(endo_2_p_neg.to_affine()),
|
||||
)?;
|
||||
p.add(layouter.namespace(|| "P + endo^2(-P)"), &endo_2_p_neg)?;
|
||||
|
||||
|
||||
@@ -95,20 +95,14 @@ impl Config {
|
||||
x_p.zip(y_p)
|
||||
.zip(x_q)
|
||||
.zip(y_q)
|
||||
.map(|(((x_p, y_p), x_q), y_q)| {
|
||||
.error_if_known_and(|(((x_p, y_p), x_q), y_q)| {
|
||||
// P is point at infinity
|
||||
if (x_p.is_zero_vartime() && y_p.is_zero_vartime())
|
||||
(x_p.is_zero_vartime() && y_p.is_zero_vartime())
|
||||
// Q is point at infinity
|
||||
|| (x_q.is_zero_vartime() && y_q.is_zero_vartime())
|
||||
// x_p = x_q
|
||||
|| (x_p == x_q)
|
||||
{
|
||||
Err(Error::Synthesis)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
.transpose()?;
|
||||
})?;
|
||||
|
||||
// Copy point `p` into `x_p`, `y_p` columns
|
||||
p.x.copy_advice(|| "x_p", region, self.x_p, offset)?;
|
||||
@@ -137,20 +131,10 @@ impl Config {
|
||||
|
||||
// Assign the sum to `x_qr`, `y_qr` columns in the next row
|
||||
let x_r = r.map(|r| r.0);
|
||||
let x_r_var = region.assign_advice(
|
||||
|| "x_r",
|
||||
self.x_qr,
|
||||
offset + 1,
|
||||
|| x_r.ok_or(Error::Synthesis),
|
||||
)?;
|
||||
let x_r_var = region.assign_advice(|| "x_r", self.x_qr, offset + 1, || x_r)?;
|
||||
|
||||
let y_r = r.map(|r| r.1);
|
||||
let y_r_var = region.assign_advice(
|
||||
|| "y_r",
|
||||
self.y_qr,
|
||||
offset + 1,
|
||||
|| y_r.ok_or(Error::Synthesis),
|
||||
)?;
|
||||
let y_r_var = region.assign_advice(|| "y_r", self.y_qr, offset + 1, || y_r)?;
|
||||
|
||||
let result = NonIdentityEccPoint {
|
||||
x: x_r_var,
|
||||
@@ -164,7 +148,10 @@ impl Config {
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use group::Curve;
|
||||
use halo2_proofs::{circuit::Layouter, plonk::Error};
|
||||
use halo2_proofs::{
|
||||
circuit::{Layouter, Value},
|
||||
plonk::Error,
|
||||
};
|
||||
use pasta_curves::pallas;
|
||||
|
||||
use crate::ecc::{EccInstructions, NonIdentityPoint};
|
||||
@@ -188,7 +175,7 @@ pub mod tests {
|
||||
let witnessed_result = NonIdentityPoint::new(
|
||||
chip,
|
||||
layouter.namespace(|| "witnessed P + Q"),
|
||||
Some((p_val + q_val).to_affine()),
|
||||
Value::known((p_val + q_val).to_affine()),
|
||||
)?;
|
||||
result.constrain_equal(layouter.namespace(|| "constrain P + Q"), &witnessed_result)?;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use std::{
|
||||
use ff::PrimeField;
|
||||
use halo2_proofs::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::{AssignedCell, Layouter, Region},
|
||||
circuit::{AssignedCell, Layouter, Region, Value},
|
||||
plonk::{Advice, Assigned, Column, ConstraintSystem, Constraints, Error, Selector},
|
||||
poly::Rotation,
|
||||
};
|
||||
@@ -270,9 +270,9 @@ impl Config {
|
||||
let real_mul = base.zip(alpha).map(|(base, alpha)| base * alpha);
|
||||
let result = result.point();
|
||||
|
||||
if let (Some(real_mul), Some(result)) = (real_mul, result) {
|
||||
assert_eq!(real_mul.to_affine(), result);
|
||||
}
|
||||
real_mul
|
||||
.zip(result)
|
||||
.assert_if_known(|(real_mul, result)| &real_mul.to_affine() == result);
|
||||
}
|
||||
|
||||
let zs = {
|
||||
@@ -327,7 +327,7 @@ impl Config {
|
||||
base: &NonIdentityEccPoint,
|
||||
acc: EccPoint,
|
||||
z_1: Z<pallas::Base>,
|
||||
lsb: Option<bool>,
|
||||
lsb: Value<bool>,
|
||||
) -> Result<(EccPoint, Z<pallas::Base>), Error> {
|
||||
// Enforce switching logic on LSB using a custom gate
|
||||
self.q_mul_lsb.enable(region, offset)?;
|
||||
@@ -343,7 +343,7 @@ impl Config {
|
||||
|| "z_0",
|
||||
self.complete_config.z_complete,
|
||||
offset + 1,
|
||||
|| z_0_val.ok_or(Error::Synthesis),
|
||||
|| z_0_val,
|
||||
)?;
|
||||
|
||||
Z(z_0_cell)
|
||||
@@ -356,39 +356,24 @@ impl Config {
|
||||
.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 {
|
||||
let x = lsb.and_then(|lsb| {
|
||||
if !lsb {
|
||||
base.x.value().cloned()
|
||||
} else {
|
||||
Some(Assigned::Zero)
|
||||
Value::known(Assigned::Zero)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
});
|
||||
|
||||
let y = if let Some(lsb) = lsb {
|
||||
let y = lsb.and_then(|lsb| {
|
||||
if !lsb {
|
||||
base.y.value().map(|y_p| -y_p)
|
||||
-base.y.value()
|
||||
} else {
|
||||
Some(Assigned::Zero)
|
||||
Value::known(Assigned::Zero)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
});
|
||||
|
||||
let x_cell = region.assign_advice(
|
||||
|| "x",
|
||||
self.add_config.x_p,
|
||||
offset,
|
||||
|| x.ok_or(Error::Synthesis),
|
||||
)?;
|
||||
|
||||
let y_cell = region.assign_advice(
|
||||
|| "y",
|
||||
self.add_config.y_p,
|
||||
offset,
|
||||
|| y.ok_or(Error::Synthesis),
|
||||
)?;
|
||||
let x_cell = region.assign_advice(|| "x", self.add_config.x_p, offset, || x)?;
|
||||
let y_cell = region.assign_advice(|| "y", self.add_config.y_p, offset, || y)?;
|
||||
|
||||
let p = EccPoint {
|
||||
x: x_cell,
|
||||
@@ -436,7 +421,7 @@ impl<F: FieldExt> Deref for Z<F> {
|
||||
}
|
||||
|
||||
// https://p.z.cash/halo2-0.1:ecc-var-mul-witness-scalar?partial
|
||||
fn decompose_for_scalar_mul(scalar: Option<&pallas::Base>) -> Vec<Option<bool>> {
|
||||
fn decompose_for_scalar_mul(scalar: Value<&pallas::Base>) -> Vec<Value<bool>> {
|
||||
construct_uint! {
|
||||
struct U256(4);
|
||||
}
|
||||
@@ -460,18 +445,16 @@ fn decompose_for_scalar_mul(scalar: Option<&pallas::Base>) -> Vec<Option<bool>>
|
||||
};
|
||||
|
||||
// Take the first 255 bits.
|
||||
bitstring.take(pallas::Scalar::NUM_BITS as usize)
|
||||
bitstring
|
||||
.take(pallas::Scalar::NUM_BITS as usize)
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
if let Some(bitstring) = bitstring {
|
||||
// Transpose.
|
||||
let mut bitstring: Vec<_> = bitstring.map(Some).collect();
|
||||
// Reverse to get the big-endian bit representation.
|
||||
bitstring.reverse();
|
||||
bitstring
|
||||
} else {
|
||||
vec![None; pallas::Scalar::NUM_BITS as usize]
|
||||
}
|
||||
// Transpose.
|
||||
let mut bitstring = bitstring.transpose_vec(pallas::Scalar::NUM_BITS as usize);
|
||||
// Reverse to get the big-endian bit representation.
|
||||
bitstring.reverse();
|
||||
bitstring
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -481,7 +464,7 @@ pub mod tests {
|
||||
Curve,
|
||||
};
|
||||
use halo2_proofs::{
|
||||
circuit::{Chip, Layouter},
|
||||
circuit::{Chip, Layouter, Value},
|
||||
plonk::Error,
|
||||
};
|
||||
use pasta_curves::pallas;
|
||||
@@ -519,7 +502,7 @@ pub mod tests {
|
||||
let expected = NonIdentityPoint::new(
|
||||
chip,
|
||||
layouter.namespace(|| "expected point"),
|
||||
Some((base_val * scalar).to_affine()),
|
||||
Value::known((base_val * scalar).to_affine()),
|
||||
)?;
|
||||
result.constrain_equal(layouter.namespace(|| "constrain result"), &expected)
|
||||
}
|
||||
@@ -531,7 +514,7 @@ pub mod tests {
|
||||
let scalar = chip.load_private(
|
||||
layouter.namespace(|| "random scalar"),
|
||||
column,
|
||||
Some(scalar_val),
|
||||
Value::known(scalar_val),
|
||||
)?;
|
||||
let scalar = ScalarVar::from_base(
|
||||
chip.clone(),
|
||||
@@ -554,8 +537,11 @@ pub mod tests {
|
||||
{
|
||||
let scalar_val = pallas::Base::zero();
|
||||
let (result, _) = {
|
||||
let scalar =
|
||||
chip.load_private(layouter.namespace(|| "zero"), column, Some(scalar_val))?;
|
||||
let scalar = chip.load_private(
|
||||
layouter.namespace(|| "zero"),
|
||||
column,
|
||||
Value::known(scalar_val),
|
||||
)?;
|
||||
let scalar = ScalarVar::from_base(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "ScalarVar from_base"),
|
||||
@@ -563,17 +549,21 @@ pub mod tests {
|
||||
)?;
|
||||
p.mul(layouter.namespace(|| "[0]B"), scalar)?
|
||||
};
|
||||
if let Some(is_identity) = result.inner().is_identity() {
|
||||
assert!(is_identity);
|
||||
}
|
||||
result
|
||||
.inner()
|
||||
.is_identity()
|
||||
.assert_if_known(|is_identity| *is_identity);
|
||||
}
|
||||
|
||||
// [-1]B (the largest possible base field element)
|
||||
{
|
||||
let scalar_val = -pallas::Base::one();
|
||||
let (result, _) = {
|
||||
let scalar =
|
||||
chip.load_private(layouter.namespace(|| "-1"), column, Some(scalar_val))?;
|
||||
let scalar = chip.load_private(
|
||||
layouter.namespace(|| "-1"),
|
||||
column,
|
||||
Value::known(scalar_val),
|
||||
)?;
|
||||
let scalar = ScalarVar::from_base(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "ScalarVar from_base"),
|
||||
|
||||
@@ -3,7 +3,7 @@ use super::{COMPLETE_RANGE, X, Y, Z};
|
||||
use crate::utilities::{bool_check, ternary};
|
||||
|
||||
use halo2_proofs::{
|
||||
circuit::Region,
|
||||
circuit::{Region, Value},
|
||||
plonk::{Advice, Column, ConstraintSystem, Constraints, Error, Expression, Selector},
|
||||
poly::Rotation,
|
||||
};
|
||||
@@ -88,7 +88,7 @@ impl Config {
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
offset: usize,
|
||||
bits: &[Option<bool>],
|
||||
bits: &[Value<bool>],
|
||||
base: &EccPoint,
|
||||
x_a: X<pallas::Base>,
|
||||
y_a: Y<pallas::Base>,
|
||||
@@ -139,15 +139,10 @@ impl Config {
|
||||
// Update `z`.
|
||||
z = {
|
||||
// z_next = z_cur * 2 + k_next
|
||||
let z_val = z.value().zip(k.as_ref()).map(|(z_val, k)| {
|
||||
pallas::Base::from(2) * z_val + pallas::Base::from(*k as u64)
|
||||
});
|
||||
let z_cell = region.assign_advice(
|
||||
|| "z",
|
||||
self.z_complete,
|
||||
row + offset + 2,
|
||||
|| z_val.ok_or(Error::Synthesis),
|
||||
)?;
|
||||
let z_val = z.value() * Value::known(pallas::Base::from(2))
|
||||
+ k.map(|k| pallas::Base::from(k as u64));
|
||||
let z_cell =
|
||||
region.assign_advice(|| "z", self.z_complete, row + offset + 2, || z_val)?;
|
||||
Z(z_cell)
|
||||
};
|
||||
zs.push(z.clone());
|
||||
@@ -177,12 +172,7 @@ impl Config {
|
||||
// fine because we are just assigning the same value to the same cell
|
||||
// twice, and then applying an equality constraint between the cell and
|
||||
// itself (which the permutation argument treats as a no-op).
|
||||
region.assign_advice(
|
||||
|| "y_p",
|
||||
self.add_config.y_p,
|
||||
row + offset,
|
||||
|| y_p.ok_or(Error::Synthesis),
|
||||
)?
|
||||
region.assign_advice(|| "y_p", self.add_config.y_p, row + offset, || y_p)?
|
||||
};
|
||||
|
||||
// U = P if the bit is set; U = -P is the bit is not set.
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::super::NonIdentityEccPoint;
|
||||
use super::{X, Y, Z};
|
||||
use crate::utilities::bool_check;
|
||||
use halo2_proofs::{
|
||||
circuit::Region,
|
||||
circuit::{Region, Value},
|
||||
plonk::{
|
||||
Advice, Column, ConstraintSystem, Constraints, Error, Expression, Selector, VirtualCells,
|
||||
},
|
||||
@@ -229,7 +229,7 @@ impl<const NUM_BITS: usize> Config<NUM_BITS> {
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
offset: usize,
|
||||
base: &NonIdentityEccPoint,
|
||||
bits: &[Option<bool>],
|
||||
bits: &[Value<bool>],
|
||||
acc: (X<pallas::Base>, Y<pallas::Base>, Z<pallas::Base>),
|
||||
) -> Result<(X<pallas::Base>, Y<pallas::Base>, Vec<Z<pallas::Base>>), Error> {
|
||||
// Check that we have the correct number of bits for this double-and-add.
|
||||
@@ -239,17 +239,16 @@ impl<const NUM_BITS: usize> Config<NUM_BITS> {
|
||||
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.is_zero_vartime() && y_p.is_zero_vartime())
|
||||
// Q is point at infinity
|
||||
|| (x_a.is_zero_vartime() && y_a.is_zero_vartime())
|
||||
// x_p = x_a
|
||||
|| (x_p == x_a)
|
||||
{
|
||||
return Err(Error::Synthesis);
|
||||
}
|
||||
}
|
||||
x_a.zip(y_a)
|
||||
.zip(x_p.zip(y_p))
|
||||
.error_if_known_and(|((x_a, y_a), (x_p, y_p))| {
|
||||
// A is point at infinity
|
||||
(x_p.is_zero_vartime() && y_p.is_zero_vartime())
|
||||
// Q is point at infinity
|
||||
|| (x_a.is_zero_vartime() && y_a.is_zero_vartime())
|
||||
// x_p = x_a
|
||||
|| (x_p == x_a)
|
||||
})?;
|
||||
|
||||
// Set q_mul values
|
||||
{
|
||||
@@ -302,27 +301,12 @@ impl<const NUM_BITS: usize> Config<NUM_BITS> {
|
||||
.value()
|
||||
.zip(k.as_ref())
|
||||
.map(|(z_val, k)| pallas::Base::from(2) * z_val + pallas::Base::from(*k as u64));
|
||||
z = region.assign_advice(
|
||||
|| "z",
|
||||
self.z,
|
||||
row + offset,
|
||||
|| z_val.ok_or(Error::Synthesis),
|
||||
)?;
|
||||
z = region.assign_advice(|| "z", self.z, row + offset, || z_val)?;
|
||||
zs.push(Z(z.clone()));
|
||||
|
||||
// Assign `x_p`, `y_p`
|
||||
region.assign_advice(
|
||||
|| "x_p",
|
||||
self.double_and_add.x_p,
|
||||
row + offset,
|
||||
|| x_p.ok_or(Error::Synthesis),
|
||||
)?;
|
||||
region.assign_advice(
|
||||
|| "y_p",
|
||||
self.y_p,
|
||||
row + offset,
|
||||
|| y_p.ok_or(Error::Synthesis),
|
||||
)?;
|
||||
region.assign_advice(|| "x_p", self.double_and_add.x_p, row + offset, || x_p)?;
|
||||
region.assign_advice(|| "y_p", self.y_p, row + offset, || y_p)?;
|
||||
|
||||
// If the bit is set, use `y`; if the bit is not set, use `-y`
|
||||
let y_p = y_p
|
||||
@@ -339,7 +323,7 @@ impl<const NUM_BITS: usize> Config<NUM_BITS> {
|
||||
|| "lambda1",
|
||||
self.double_and_add.lambda_1,
|
||||
row + offset,
|
||||
|| lambda1.ok_or(Error::Synthesis),
|
||||
|| lambda1,
|
||||
)?;
|
||||
|
||||
// x_R = λ1^2 - x_A - x_P
|
||||
@@ -361,25 +345,18 @@ impl<const NUM_BITS: usize> Config<NUM_BITS> {
|
||||
|| "lambda2",
|
||||
self.double_and_add.lambda_2,
|
||||
row + offset,
|
||||
|| lambda2.ok_or(Error::Synthesis),
|
||||
|| lambda2,
|
||||
)?;
|
||||
|
||||
// Compute and assign `x_a` for the next row
|
||||
let x_a_new = lambda2
|
||||
.zip(x_a.value())
|
||||
.zip(x_r)
|
||||
.map(|((lambda2, x_a), x_r)| lambda2.square() - x_a - x_r);
|
||||
y_a = lambda2
|
||||
.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_new = lambda2.square() - x_a.value() - x_r;
|
||||
y_a = lambda2 * (x_a.value() - x_a_new) - y_a;
|
||||
let x_a_val = x_a_new;
|
||||
x_a = region.assign_advice(
|
||||
|| "x_a",
|
||||
self.double_and_add.x_a,
|
||||
row + offset + 1,
|
||||
|| x_a_val.ok_or(Error::Synthesis),
|
||||
|| x_a_val,
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -388,7 +365,7 @@ impl<const NUM_BITS: usize> Config<NUM_BITS> {
|
||||
|| "y_a",
|
||||
self.double_and_add.lambda_1,
|
||||
offset + NUM_BITS,
|
||||
|| y_a.ok_or(Error::Synthesis),
|
||||
|| y_a,
|
||||
)?;
|
||||
|
||||
Ok((X(x_a), Y(y_a), zs))
|
||||
|
||||
@@ -121,7 +121,7 @@ impl Config {
|
||||
|| "s = alpha + k_254 ⋅ 2^130",
|
||||
self.advices[0],
|
||||
0,
|
||||
|| s_val.ok_or(Error::Synthesis),
|
||||
|| s_val,
|
||||
)
|
||||
},
|
||||
)?
|
||||
@@ -153,7 +153,7 @@ impl Config {
|
||||
|| "η = inv0(z_130)",
|
||||
self.advices[0],
|
||||
offset + 2,
|
||||
|| eta.ok_or(Error::Synthesis),
|
||||
|| eta,
|
||||
)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ use group::{
|
||||
Curve,
|
||||
};
|
||||
use halo2_proofs::{
|
||||
circuit::{AssignedCell, Region},
|
||||
circuit::{AssignedCell, Region, Value},
|
||||
plonk::{
|
||||
Advice, Column, ConstraintSystem, Constraints, Error, Expression, Fixed, Selector,
|
||||
VirtualCells,
|
||||
@@ -236,7 +236,7 @@ impl<FixedPoints: super::FixedPoints<pallas::Affine>> Config<FixedPoints> {
|
||||
constants = Some(build_constants());
|
||||
}
|
||||
let lagrange_coeffs = &constants.as_ref().unwrap().0;
|
||||
Ok(lagrange_coeffs[window][k])
|
||||
Value::known(lagrange_coeffs[window][k])
|
||||
},
|
||||
)?;
|
||||
}
|
||||
@@ -248,7 +248,7 @@ impl<FixedPoints: super::FixedPoints<pallas::Affine>> Config<FixedPoints> {
|
||||
window + offset,
|
||||
|| {
|
||||
let z = &constants.as_ref().unwrap().1;
|
||||
Ok(pallas::Base::from(z[window]))
|
||||
Value::known(pallas::Base::from(z[window]))
|
||||
},
|
||||
)?;
|
||||
}
|
||||
@@ -262,8 +262,8 @@ impl<FixedPoints: super::FixedPoints<pallas::Affine>> Config<FixedPoints> {
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
offset: usize,
|
||||
w: usize,
|
||||
k_usize: Option<usize>,
|
||||
window_scalar: Option<pallas::Scalar>,
|
||||
k_usize: Value<usize>,
|
||||
window_scalar: Value<pallas::Scalar>,
|
||||
base: &F,
|
||||
) -> Result<NonIdentityEccPoint, Error> {
|
||||
let base_value = base.generator();
|
||||
@@ -284,7 +284,7 @@ impl<FixedPoints: super::FixedPoints<pallas::Affine>> Config<FixedPoints> {
|
||||
|| format!("mul_b_x, window {}", w),
|
||||
self.add_config.x_p,
|
||||
offset + w,
|
||||
|| x.ok_or(Error::Synthesis),
|
||||
|| x,
|
||||
)?;
|
||||
|
||||
let y = mul_b.map(|mul_b| {
|
||||
@@ -296,7 +296,7 @@ impl<FixedPoints: super::FixedPoints<pallas::Affine>> Config<FixedPoints> {
|
||||
|| format!("mul_b_y, window {}", w),
|
||||
self.add_config.y_p,
|
||||
offset + w,
|
||||
|| y.ok_or(Error::Synthesis),
|
||||
|| y,
|
||||
)?;
|
||||
|
||||
NonIdentityEccPoint { x, y }
|
||||
@@ -304,7 +304,7 @@ impl<FixedPoints: super::FixedPoints<pallas::Affine>> Config<FixedPoints> {
|
||||
|
||||
// Assign u = (y_p + z_w).sqrt()
|
||||
let u_val = k_usize.map(|k| pallas::Base::from_repr(base_u[w][k]).unwrap());
|
||||
region.assign_advice(|| "u", self.u, offset + w, || u_val.ok_or(Error::Synthesis))?;
|
||||
region.assign_advice(|| "u", self.u, offset + w, || u_val)?;
|
||||
|
||||
Ok(mul_b)
|
||||
}
|
||||
@@ -370,8 +370,8 @@ impl<FixedPoints: super::FixedPoints<pallas::Affine>> Config<FixedPoints> {
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
offset: usize,
|
||||
w: usize,
|
||||
k: Option<pallas::Scalar>,
|
||||
k_usize: Option<usize>,
|
||||
k: Value<pallas::Scalar>,
|
||||
k_usize: Value<usize>,
|
||||
base: &F,
|
||||
) -> Result<NonIdentityEccPoint, Error> {
|
||||
// `scalar = [(k_w + 2) ⋅ 8^w]
|
||||
@@ -440,15 +440,13 @@ impl ScalarFixed {
|
||||
///
|
||||
/// This function does not require that the base field fits inside the scalar field,
|
||||
/// because the window size fits into either field.
|
||||
fn windows_field(&self) -> Vec<Option<pallas::Scalar>> {
|
||||
fn windows_field(&self) -> Vec<Value<pallas::Scalar>> {
|
||||
let running_sum_to_windows = |zs: Vec<AssignedCell<pallas::Base, pallas::Base>>| {
|
||||
(0..(zs.len() - 1))
|
||||
.map(|idx| {
|
||||
let z_cur = zs[idx].value();
|
||||
let z_next = zs[idx + 1].value();
|
||||
let word = z_cur
|
||||
.zip(z_next)
|
||||
.map(|(z_cur, z_next)| z_cur - z_next * *H_BASE);
|
||||
let word = z_cur - z_next * Value::known(*H_BASE);
|
||||
// This assumes that the endianness of the encodings of pallas::Base
|
||||
// and pallas::Scalar are the same. They happen to be, but we need to
|
||||
// be careful if this is generalised.
|
||||
@@ -484,7 +482,7 @@ impl ScalarFixed {
|
||||
/// The scalar decomposition is guaranteed to be in three-bit windows, so we construct
|
||||
/// `usize` indices from the lowest three bits of each window field element for
|
||||
/// convenient indexing into `u`-values.
|
||||
fn windows_usize(&self) -> Vec<Option<usize>> {
|
||||
fn windows_usize(&self) -> Vec<Value<usize>> {
|
||||
self.windows_field()
|
||||
.iter()
|
||||
.map(|window| {
|
||||
|
||||
@@ -227,9 +227,9 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
|
||||
let real_mul = scalar.map(|scalar| base.generator() * scalar);
|
||||
let result = result.point();
|
||||
|
||||
if let (Some(real_mul), Some(result)) = (real_mul, result) {
|
||||
assert_eq!(real_mul.to_affine(), result);
|
||||
}
|
||||
real_mul
|
||||
.zip(result)
|
||||
.assert_if_known(|(real_mul, result)| &real_mul.to_affine() == result);
|
||||
}
|
||||
|
||||
// We want to enforce canonicity of a 255-bit base field element, α.
|
||||
@@ -326,7 +326,7 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
|
||||
|| "α_1 = α[252..=253]",
|
||||
self.canon_advices[1],
|
||||
offset,
|
||||
|| alpha_1.ok_or(Error::Synthesis),
|
||||
|| alpha_1,
|
||||
)?;
|
||||
|
||||
// Witness the MSB α_2 = α[254]
|
||||
@@ -335,7 +335,7 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
|
||||
|| "α_2 = α[254]",
|
||||
self.canon_advices[2],
|
||||
offset,
|
||||
|| alpha_2.ok_or(Error::Synthesis),
|
||||
|| alpha_2,
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -382,7 +382,7 @@ pub mod tests {
|
||||
Curve,
|
||||
};
|
||||
use halo2_proofs::{
|
||||
circuit::{Chip, Layouter},
|
||||
circuit::{Chip, Layouter, Value},
|
||||
plonk::Error,
|
||||
};
|
||||
use pasta_curves::pallas;
|
||||
@@ -432,7 +432,7 @@ pub mod tests {
|
||||
let expected = NonIdentityPoint::new(
|
||||
chip,
|
||||
layouter.namespace(|| "expected point"),
|
||||
Some((base_val * scalar).to_affine()),
|
||||
Value::known((base_val * scalar).to_affine()),
|
||||
)?;
|
||||
result.constrain_equal(layouter.namespace(|| "constrain result"), &expected)
|
||||
}
|
||||
@@ -444,7 +444,7 @@ pub mod tests {
|
||||
let scalar_fixed = chip.load_private(
|
||||
layouter.namespace(|| "random base field element"),
|
||||
column,
|
||||
Some(scalar_fixed),
|
||||
Value::known(scalar_fixed),
|
||||
)?;
|
||||
base.mul(layouter.namespace(|| "random [a]B"), scalar_fixed)?
|
||||
};
|
||||
@@ -472,7 +472,7 @@ pub mod tests {
|
||||
let scalar_fixed = chip.load_private(
|
||||
layouter.namespace(|| "mul with double"),
|
||||
column,
|
||||
Some(scalar_fixed),
|
||||
Value::known(scalar_fixed),
|
||||
)?;
|
||||
base.mul(layouter.namespace(|| "mul with double"), scalar_fixed)?
|
||||
};
|
||||
@@ -490,21 +490,28 @@ pub mod tests {
|
||||
{
|
||||
let scalar_fixed = pallas::Base::zero();
|
||||
let result = {
|
||||
let scalar_fixed =
|
||||
chip.load_private(layouter.namespace(|| "zero"), column, Some(scalar_fixed))?;
|
||||
let scalar_fixed = chip.load_private(
|
||||
layouter.namespace(|| "zero"),
|
||||
column,
|
||||
Value::known(scalar_fixed),
|
||||
)?;
|
||||
base.mul(layouter.namespace(|| "mul by zero"), scalar_fixed)?
|
||||
};
|
||||
if let Some(is_identity) = result.inner().is_identity() {
|
||||
assert!(is_identity);
|
||||
}
|
||||
result
|
||||
.inner()
|
||||
.is_identity()
|
||||
.assert_if_known(|is_identity| *is_identity);
|
||||
}
|
||||
|
||||
// [-1]B is the largest base field element
|
||||
{
|
||||
let scalar_fixed = -pallas::Base::one();
|
||||
let result = {
|
||||
let scalar_fixed =
|
||||
chip.load_private(layouter.namespace(|| "-1"), column, Some(scalar_fixed))?;
|
||||
let scalar_fixed = chip.load_private(
|
||||
layouter.namespace(|| "-1"),
|
||||
column,
|
||||
Value::known(scalar_fixed),
|
||||
)?;
|
||||
base.mul(layouter.namespace(|| "mul by -1"), scalar_fixed)?
|
||||
};
|
||||
constrain_equal_non_id(
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::utilities::{decompose_word, range_check};
|
||||
use arrayvec::ArrayVec;
|
||||
use ff::PrimeField;
|
||||
use halo2_proofs::{
|
||||
circuit::{AssignedCell, Layouter, Region},
|
||||
circuit::{AssignedCell, Layouter, Region, Value},
|
||||
plonk::{ConstraintSystem, Constraints, Error, Selector},
|
||||
poly::Rotation,
|
||||
};
|
||||
@@ -57,7 +57,7 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
offset: usize,
|
||||
scalar: Option<pallas::Scalar>,
|
||||
scalar: Value<pallas::Scalar>,
|
||||
) -> Result<EccScalarFixed, Error> {
|
||||
let windows = self.decompose_scalar_fixed::<{ pallas::Scalar::NUM_BITS as usize }>(
|
||||
scalar, offset, region,
|
||||
@@ -74,7 +74,7 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
|
||||
/// The scalar is allowed to be non-canonical.
|
||||
fn decompose_scalar_fixed<const SCALAR_NUM_BITS: usize>(
|
||||
&self,
|
||||
scalar: Option<pallas::Scalar>,
|
||||
scalar: Value<pallas::Scalar>,
|
||||
offset: usize,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
) -> Result<ArrayVec<AssignedCell<pallas::Base, pallas::Base>, NUM_WINDOWS>, Error> {
|
||||
@@ -84,20 +84,18 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
|
||||
}
|
||||
|
||||
// Decompose scalar into `k-bit` windows
|
||||
let scalar_windows: Option<Vec<u8>> = scalar.map(|scalar| {
|
||||
let scalar_windows: Value<Vec<u8>> = scalar.map(|scalar| {
|
||||
decompose_word::<pallas::Scalar>(&scalar, SCALAR_NUM_BITS, FIXED_BASE_WINDOW_SIZE)
|
||||
});
|
||||
|
||||
// Transpose `Option<Vec<u8>>` into `Vec<Option<pallas::Base>>`.
|
||||
let scalar_windows: Vec<Option<pallas::Base>> = if let Some(windows) = scalar_windows {
|
||||
assert_eq!(windows.len(), NUM_WINDOWS);
|
||||
windows
|
||||
.into_iter()
|
||||
.map(|window| Some(pallas::Base::from(window as u64)))
|
||||
.collect()
|
||||
} else {
|
||||
vec![None; NUM_WINDOWS]
|
||||
};
|
||||
// Transpose `Value<Vec<u8>>` into `Vec<Value<pallas::Base>>`.
|
||||
let scalar_windows = scalar_windows
|
||||
.map(|windows| {
|
||||
windows
|
||||
.into_iter()
|
||||
.map(|window| pallas::Base::from(window as u64))
|
||||
})
|
||||
.transpose_vec(NUM_WINDOWS);
|
||||
|
||||
// Store the scalar decomposition
|
||||
let mut windows: ArrayVec<AssignedCell<pallas::Base, pallas::Base>, NUM_WINDOWS> =
|
||||
@@ -107,7 +105,7 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
|
||||
|| format!("k[{:?}]", offset + idx),
|
||||
self.super_config.window,
|
||||
offset + idx,
|
||||
|| window.ok_or(Error::Synthesis),
|
||||
|| window,
|
||||
)?;
|
||||
windows.push(window_cell);
|
||||
}
|
||||
@@ -170,9 +168,9 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
|
||||
let real_mul = scalar.value.map(|scalar| base.generator() * scalar);
|
||||
let result = result.point();
|
||||
|
||||
if let (Some(real_mul), Some(result)) = (real_mul, result) {
|
||||
assert_eq!(real_mul.to_affine(), result);
|
||||
}
|
||||
real_mul
|
||||
.zip(result)
|
||||
.assert_if_known(|(real_mul, result)| &real_mul.to_affine() == result);
|
||||
}
|
||||
|
||||
Ok((result, scalar))
|
||||
@@ -182,7 +180,10 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use group::{ff::Field, Curve};
|
||||
use halo2_proofs::{circuit::Layouter, plonk::Error};
|
||||
use halo2_proofs::{
|
||||
circuit::{Layouter, Value},
|
||||
plonk::Error,
|
||||
};
|
||||
use pasta_curves::pallas;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
@@ -224,7 +225,7 @@ pub mod tests {
|
||||
let expected = NonIdentityPoint::new(
|
||||
chip,
|
||||
layouter.namespace(|| "expected point"),
|
||||
Some((base_val * scalar_val).to_affine()),
|
||||
Value::known((base_val * scalar_val).to_affine()),
|
||||
)?;
|
||||
result.constrain_equal(layouter.namespace(|| "constrain result"), &expected)
|
||||
}
|
||||
@@ -235,7 +236,7 @@ pub mod tests {
|
||||
let by = ScalarFixed::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "random a"),
|
||||
Some(scalar_fixed),
|
||||
Value::known(scalar_fixed),
|
||||
)?;
|
||||
|
||||
let (result, _) = base.mul(layouter.namespace(|| "random [a]B"), by)?;
|
||||
@@ -263,7 +264,7 @@ pub mod tests {
|
||||
let by = ScalarFixed::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| LAST_DOUBLING),
|
||||
Some(scalar_fixed),
|
||||
Value::known(scalar_fixed),
|
||||
)?;
|
||||
let (result, _) = base.mul(layouter.namespace(|| "mul with double"), by)?;
|
||||
|
||||
@@ -280,12 +281,16 @@ pub mod tests {
|
||||
// on the last step.
|
||||
{
|
||||
let scalar_fixed = pallas::Scalar::zero();
|
||||
let zero =
|
||||
ScalarFixed::new(chip.clone(), layouter.namespace(|| "0"), Some(scalar_fixed))?;
|
||||
let zero = ScalarFixed::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "0"),
|
||||
Value::known(scalar_fixed),
|
||||
)?;
|
||||
let (result, _) = base.mul(layouter.namespace(|| "mul by zero"), zero)?;
|
||||
if let Some(is_identity) = result.inner().is_identity() {
|
||||
assert!(is_identity);
|
||||
}
|
||||
result
|
||||
.inner()
|
||||
.is_identity()
|
||||
.assert_if_known(|is_identity| *is_identity);
|
||||
}
|
||||
|
||||
// [-1]B is the largest scalar field element.
|
||||
@@ -294,7 +299,7 @@ pub mod tests {
|
||||
let neg_1 = ScalarFixed::new(
|
||||
chip.clone(),
|
||||
layouter.namespace(|| "-1"),
|
||||
Some(scalar_fixed),
|
||||
Value::known(scalar_fixed),
|
||||
)?;
|
||||
let (result, _) = base.mul(layouter.namespace(|| "mul by -1"), neg_1)?;
|
||||
constrain_equal_non_id(
|
||||
|
||||
@@ -175,15 +175,13 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
|
||||
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() {
|
||||
let y_val = sign.value().and_then(|sign| {
|
||||
if sign == &-pallas::Base::one() {
|
||||
magnitude_mul.y.value().cloned().map(|y| -y)
|
||||
-magnitude_mul.y.value()
|
||||
} else {
|
||||
magnitude_mul.y.value().cloned()
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
});
|
||||
|
||||
// Enable mul_fixed_short selector on final row
|
||||
self.q_mul_fixed_short.enable(&mut region, offset)?;
|
||||
@@ -193,7 +191,7 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
|
||||
|| "y_var",
|
||||
self.super_config.add_config.y_p,
|
||||
offset,
|
||||
|| y_val.ok_or(Error::Synthesis),
|
||||
|| y_val,
|
||||
)?;
|
||||
|
||||
Ok(EccPoint {
|
||||
@@ -213,34 +211,35 @@ impl<Fixed: FixedPoints<pallas::Affine>> Config<Fixed> {
|
||||
use super::super::FixedPoint;
|
||||
use group::{ff::PrimeField, Curve};
|
||||
|
||||
if let (Some(magnitude), Some(sign)) = (scalar.magnitude.value(), scalar.sign.value()) {
|
||||
let magnitude_is_valid = magnitude <= &pallas::Base::from(0xFFFF_FFFF_FFFF_FFFFu64);
|
||||
let sign_is_valid = sign * sign == pallas::Base::one();
|
||||
if magnitude_is_valid && sign_is_valid {
|
||||
let scalar = scalar.magnitude.value().zip(scalar.sign.value()).map(
|
||||
|(magnitude, sign)| {
|
||||
scalar
|
||||
.magnitude
|
||||
.value()
|
||||
.zip(scalar.sign.value())
|
||||
.zip(result.point())
|
||||
.assert_if_known(|((magnitude, sign), result)| {
|
||||
let magnitude_is_valid =
|
||||
magnitude <= &&pallas::Base::from(0xFFFF_FFFF_FFFF_FFFFu64);
|
||||
let sign_is_valid = sign.square() == pallas::Base::one();
|
||||
// Only check the result if the magnitude and sign are valid.
|
||||
!(magnitude_is_valid && sign_is_valid) || {
|
||||
let scalar = {
|
||||
// Move magnitude from base field into scalar field (which always fits
|
||||
// for Pallas).
|
||||
let magnitude = pallas::Scalar::from_repr(magnitude.to_repr()).unwrap();
|
||||
|
||||
let sign = if sign == &pallas::Base::one() {
|
||||
let sign = if sign == &&pallas::Base::one() {
|
||||
pallas::Scalar::one()
|
||||
} else {
|
||||
-pallas::Scalar::one()
|
||||
};
|
||||
|
||||
magnitude * sign
|
||||
},
|
||||
);
|
||||
let real_mul = scalar.map(|scalar| base.generator() * scalar);
|
||||
};
|
||||
let real_mul = base.generator() * scalar;
|
||||
|
||||
let result = result.point();
|
||||
|
||||
if let (Some(real_mul), Some(result)) = (real_mul, result) {
|
||||
assert_eq!(real_mul.to_affine(), result);
|
||||
&real_mul.to_affine() == result
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ok((result, scalar))
|
||||
@@ -252,7 +251,7 @@ pub mod tests {
|
||||
use group::{ff::PrimeField, Curve};
|
||||
use halo2_proofs::{
|
||||
arithmetic::CurveAffine,
|
||||
circuit::{AssignedCell, Chip, Layouter},
|
||||
circuit::{AssignedCell, Chip, Layouter, Value},
|
||||
plonk::{Any, Error},
|
||||
};
|
||||
use pasta_curves::{arithmetic::FieldExt, pallas};
|
||||
@@ -282,9 +281,13 @@ pub mod tests {
|
||||
sign: pallas::Base,
|
||||
) -> Result<MagnitudeSign, Error> {
|
||||
let column = chip.config().advices[0];
|
||||
let magnitude =
|
||||
chip.load_private(layouter.namespace(|| "magnitude"), column, Some(magnitude))?;
|
||||
let sign = chip.load_private(layouter.namespace(|| "sign"), column, Some(sign))?;
|
||||
let magnitude = chip.load_private(
|
||||
layouter.namespace(|| "magnitude"),
|
||||
column,
|
||||
Value::known(magnitude),
|
||||
)?;
|
||||
let sign =
|
||||
chip.load_private(layouter.namespace(|| "sign"), column, Value::known(sign))?;
|
||||
|
||||
Ok((magnitude, sign))
|
||||
}
|
||||
@@ -299,7 +302,7 @@ pub mod tests {
|
||||
let expected = NonIdentityPoint::new(
|
||||
chip,
|
||||
layouter.namespace(|| "expected point"),
|
||||
Some((base_val * scalar_val).to_affine()),
|
||||
Value::known((base_val * scalar_val).to_affine()),
|
||||
)?;
|
||||
result.constrain_equal(layouter.namespace(|| "constrain result"), &expected)
|
||||
}
|
||||
@@ -391,9 +394,10 @@ pub mod tests {
|
||||
)?;
|
||||
test_short.mul(layouter.namespace(|| *name), by)?
|
||||
};
|
||||
if let Some(is_identity) = result.inner().is_identity() {
|
||||
assert!(is_identity);
|
||||
}
|
||||
result
|
||||
.inner()
|
||||
.is_identity()
|
||||
.assert_if_known(|is_identity| *is_identity);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -413,10 +417,10 @@ pub mod tests {
|
||||
|
||||
#[derive(Default)]
|
||||
struct MyCircuit {
|
||||
magnitude: Option<pallas::Base>,
|
||||
sign: Option<pallas::Base>,
|
||||
magnitude: Value<pallas::Base>,
|
||||
sign: Value<pallas::Base>,
|
||||
// For test checking
|
||||
magnitude_error: Option<pallas::Base>,
|
||||
magnitude_error: Value<pallas::Base>,
|
||||
}
|
||||
|
||||
impl UtilitiesInstructions<pallas::Base> for MyCircuit {
|
||||
@@ -517,41 +521,41 @@ pub mod tests {
|
||||
let circuits = [
|
||||
// 2^64
|
||||
MyCircuit {
|
||||
magnitude: Some(pallas::Base::from_u128(1 << 64)),
|
||||
sign: Some(pallas::Base::one()),
|
||||
magnitude_error: Some(pallas::Base::from(1 << 1)),
|
||||
magnitude: Value::known(pallas::Base::from_u128(1 << 64)),
|
||||
sign: Value::known(pallas::Base::one()),
|
||||
magnitude_error: Value::known(pallas::Base::from(1 << 1)),
|
||||
},
|
||||
// -2^64
|
||||
MyCircuit {
|
||||
magnitude: Some(pallas::Base::from_u128(1 << 64)),
|
||||
sign: Some(-pallas::Base::one()),
|
||||
magnitude_error: Some(pallas::Base::from(1 << 1)),
|
||||
magnitude: Value::known(pallas::Base::from_u128(1 << 64)),
|
||||
sign: Value::known(-pallas::Base::one()),
|
||||
magnitude_error: Value::known(pallas::Base::from(1 << 1)),
|
||||
},
|
||||
// 2^66
|
||||
MyCircuit {
|
||||
magnitude: Some(pallas::Base::from_u128(1 << 66)),
|
||||
sign: Some(pallas::Base::one()),
|
||||
magnitude_error: Some(pallas::Base::from(1 << 3)),
|
||||
magnitude: Value::known(pallas::Base::from_u128(1 << 66)),
|
||||
sign: Value::known(pallas::Base::one()),
|
||||
magnitude_error: Value::known(pallas::Base::from(1 << 3)),
|
||||
},
|
||||
// -2^66
|
||||
MyCircuit {
|
||||
magnitude: Some(pallas::Base::from_u128(1 << 66)),
|
||||
sign: Some(-pallas::Base::one()),
|
||||
magnitude_error: Some(pallas::Base::from(1 << 3)),
|
||||
magnitude: Value::known(pallas::Base::from_u128(1 << 66)),
|
||||
sign: Value::known(-pallas::Base::one()),
|
||||
magnitude_error: Value::known(pallas::Base::from(1 << 3)),
|
||||
},
|
||||
// 2^254
|
||||
MyCircuit {
|
||||
magnitude: Some(pallas::Base::from_u128(1 << 127).square()),
|
||||
sign: Some(pallas::Base::one()),
|
||||
magnitude_error: Some(
|
||||
magnitude: Value::known(pallas::Base::from_u128(1 << 127).square()),
|
||||
sign: Value::known(pallas::Base::one()),
|
||||
magnitude_error: Value::known(
|
||||
pallas::Base::from_u128(1 << 95).square() * pallas::Base::from(2),
|
||||
),
|
||||
},
|
||||
// -2^254
|
||||
MyCircuit {
|
||||
magnitude: Some(pallas::Base::from_u128(1 << 127).square()),
|
||||
sign: Some(-pallas::Base::one()),
|
||||
magnitude_error: Some(
|
||||
magnitude: Value::known(pallas::Base::from_u128(1 << 127).square()),
|
||||
sign: Value::known(-pallas::Base::one()),
|
||||
magnitude_error: Value::known(
|
||||
pallas::Base::from_u128(1 << 95).square() * pallas::Base::from(2),
|
||||
),
|
||||
},
|
||||
@@ -559,38 +563,43 @@ pub mod tests {
|
||||
|
||||
for circuit in circuits.iter() {
|
||||
let prover = MockProver::<pallas::Base>::run(11, circuit, vec![]).unwrap();
|
||||
assert_eq!(
|
||||
prover.verify(),
|
||||
Err(vec![
|
||||
VerifyFailure::ConstraintNotSatisfied {
|
||||
constraint: (
|
||||
(17, "Short fixed-base mul gate").into(),
|
||||
0,
|
||||
"last_window_check"
|
||||
)
|
||||
.into(),
|
||||
location: FailureLocation::InRegion {
|
||||
region: (3, "Short fixed-base mul (most significant word)").into(),
|
||||
offset: 1,
|
||||
circuit.magnitude_error.assert_if_known(|magnitude_error| {
|
||||
assert_eq!(
|
||||
prover.verify(),
|
||||
Err(vec![
|
||||
VerifyFailure::ConstraintNotSatisfied {
|
||||
constraint: (
|
||||
(17, "Short fixed-base mul gate").into(),
|
||||
0,
|
||||
"last_window_check",
|
||||
)
|
||||
.into(),
|
||||
location: FailureLocation::InRegion {
|
||||
region: (3, "Short fixed-base mul (most significant word)")
|
||||
.into(),
|
||||
offset: 1,
|
||||
},
|
||||
cell_values: vec![(
|
||||
((Any::Advice, 5).into(), 0).into(),
|
||||
format_value(*magnitude_error),
|
||||
)],
|
||||
},
|
||||
cell_values: vec![(
|
||||
((Any::Advice, 5).into(), 0).into(),
|
||||
format_value(circuit.magnitude_error.unwrap()),
|
||||
)],
|
||||
},
|
||||
VerifyFailure::Permutation {
|
||||
column: (Any::Fixed, 9).into(),
|
||||
location: FailureLocation::OutsideRegion { row: 0 },
|
||||
},
|
||||
VerifyFailure::Permutation {
|
||||
column: (Any::Advice, 4).into(),
|
||||
location: FailureLocation::InRegion {
|
||||
region: (2, "Short fixed-base mul (incomplete addition)").into(),
|
||||
offset: 22,
|
||||
VerifyFailure::Permutation {
|
||||
column: (Any::Fixed, 9).into(),
|
||||
location: FailureLocation::OutsideRegion { row: 0 },
|
||||
},
|
||||
}
|
||||
])
|
||||
);
|
||||
VerifyFailure::Permutation {
|
||||
column: (Any::Advice, 4).into(),
|
||||
location: FailureLocation::InRegion {
|
||||
region: (2, "Short fixed-base mul (incomplete addition)")
|
||||
.into(),
|
||||
offset: 22,
|
||||
},
|
||||
},
|
||||
])
|
||||
);
|
||||
true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -598,9 +607,9 @@ pub mod tests {
|
||||
{
|
||||
let magnitude_u64 = rand::random::<u64>();
|
||||
let circuit = MyCircuit {
|
||||
magnitude: Some(pallas::Base::from(magnitude_u64)),
|
||||
sign: Some(pallas::Base::zero()),
|
||||
magnitude_error: None,
|
||||
magnitude: Value::known(pallas::Base::from(magnitude_u64)),
|
||||
sign: Value::known(pallas::Base::zero()),
|
||||
magnitude_error: Value::unknown(),
|
||||
};
|
||||
|
||||
let negation_check_y = {
|
||||
|
||||
@@ -3,7 +3,7 @@ use super::{EccPoint, NonIdentityEccPoint};
|
||||
use group::prime::PrimeCurveAffine;
|
||||
|
||||
use halo2_proofs::{
|
||||
circuit::{AssignedCell, Region},
|
||||
circuit::{AssignedCell, Region, Value},
|
||||
plonk::{
|
||||
Advice, Assigned, Column, ConstraintSystem, Constraints, Error, Expression, Selector,
|
||||
VirtualCells,
|
||||
@@ -87,19 +87,17 @@ impl Config {
|
||||
|
||||
fn assign_xy(
|
||||
&self,
|
||||
value: Option<(Assigned<pallas::Base>, Assigned<pallas::Base>)>,
|
||||
value: Value<(Assigned<pallas::Base>, Assigned<pallas::Base>)>,
|
||||
offset: usize,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
) -> Result<Coordinates, Error> {
|
||||
// Assign `x` value
|
||||
let x_val = value.map(|value| value.0);
|
||||
let x_var =
|
||||
region.assign_advice(|| "x", self.x, offset, || x_val.ok_or(Error::Synthesis))?;
|
||||
let x_var = region.assign_advice(|| "x", self.x, offset, || x_val)?;
|
||||
|
||||
// Assign `y` value
|
||||
let y_val = value.map(|value| value.1);
|
||||
let y_var =
|
||||
region.assign_advice(|| "y", self.y, offset, || y_val.ok_or(Error::Synthesis))?;
|
||||
let y_var = region.assign_advice(|| "y", self.y, offset, || y_val)?;
|
||||
|
||||
Ok((x_var, y_var))
|
||||
}
|
||||
@@ -107,7 +105,7 @@ impl Config {
|
||||
/// Assigns a point that can be the identity.
|
||||
pub(super) fn point(
|
||||
&self,
|
||||
value: Option<pallas::Affine>,
|
||||
value: Value<pallas::Affine>,
|
||||
offset: usize,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
) -> Result<EccPoint, Error> {
|
||||
@@ -131,19 +129,15 @@ impl Config {
|
||||
/// Assigns a non-identity point.
|
||||
pub(super) fn point_non_id(
|
||||
&self,
|
||||
value: Option<pallas::Affine>,
|
||||
value: Value<pallas::Affine>,
|
||||
offset: usize,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
) -> Result<NonIdentityEccPoint, Error> {
|
||||
// Enable `q_point_non_id` selector
|
||||
self.q_point_non_id.enable(region, offset)?;
|
||||
|
||||
if let Some(value) = value {
|
||||
// Return an error if the point is the identity.
|
||||
if value == pallas::Affine::identity() {
|
||||
return Err(Error::Synthesis);
|
||||
}
|
||||
};
|
||||
// Return an error if the point is the identity.
|
||||
value.error_if_known_and(|value| value == &pallas::Affine::identity())?;
|
||||
|
||||
let value = value.map(|value| {
|
||||
let value = value.coordinates().unwrap();
|
||||
@@ -173,7 +167,7 @@ pub mod tests {
|
||||
NonIdentityPoint::new(
|
||||
chip,
|
||||
layouter.namespace(|| "witness identity"),
|
||||
Some(pallas::Affine::identity()),
|
||||
Value::known(pallas::Affine::identity()),
|
||||
)
|
||||
.expect_err("witnessing 𝒪 should return an error");
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::iter;
|
||||
|
||||
use halo2_proofs::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::{AssignedCell, Cell, Chip, Layouter, Region},
|
||||
circuit::{AssignedCell, Cell, Chip, Layouter, Region, Value},
|
||||
plonk::{
|
||||
Advice, Any, Column, ConstraintSystem, Constraints, Error, Expression, Fixed, Selector,
|
||||
},
|
||||
@@ -349,7 +349,7 @@ impl<
|
||||
|| format!("load pad_{}", i),
|
||||
config.rc_b[i],
|
||||
1,
|
||||
|| Ok(padding_value),
|
||||
|| Value::known(padding_value),
|
||||
)?,
|
||||
_ => panic!("Input is not padded"),
|
||||
};
|
||||
@@ -367,20 +367,18 @@ impl<
|
||||
|
||||
// Constrain the output.
|
||||
let constrain_output_word = |i: usize| {
|
||||
let value = initial_state[i].0.value().and_then(|initial_word| {
|
||||
input
|
||||
let value = initial_state[i].0.value().copied()
|
||||
+ input
|
||||
.get(i)
|
||||
.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)
|
||||
});
|
||||
.unwrap_or_else(|| Value::known(F::zero()));
|
||||
region
|
||||
.assign_advice(
|
||||
|| format!("load output_{}", i),
|
||||
config.state[i],
|
||||
2,
|
||||
|| value.ok_or(Error::Synthesis),
|
||||
|| value,
|
||||
)
|
||||
.map(StateWord)
|
||||
};
|
||||
@@ -424,7 +422,7 @@ impl<F: FieldExt> Var<F> for StateWord<F> {
|
||||
self.0.cell()
|
||||
}
|
||||
|
||||
fn value(&self) -> Option<F> {
|
||||
fn value(&self) -> Value<F> {
|
||||
self.0.value().cloned()
|
||||
}
|
||||
}
|
||||
@@ -446,7 +444,7 @@ impl<F: FieldExt, const WIDTH: usize> Pow5State<F, WIDTH> {
|
||||
.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 r: Value<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| {
|
||||
r.as_ref().map(|r| {
|
||||
@@ -469,9 +467,9 @@ 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.0.value().cloned()).collect();
|
||||
let p: Value<Vec<_>> = self.0.iter().map(|word| word.0.value().cloned()).collect();
|
||||
|
||||
let r: Option<Vec<_>> = p.map(|p| {
|
||||
let r: Value<Vec<_>> = p.map(|p| {
|
||||
let r_0 = (p[0] + config.round_constants[round][0]).pow(&config.alpha);
|
||||
let r_i = p[1..]
|
||||
.iter()
|
||||
@@ -484,10 +482,10 @@ impl<F: FieldExt, const WIDTH: usize> Pow5State<F, WIDTH> {
|
||||
|| format!("round_{} partial_sbox", round),
|
||||
config.partial_sbox,
|
||||
offset,
|
||||
|| r.as_ref().map(|r| r[0]).ok_or(Error::Synthesis),
|
||||
|| r.as_ref().map(|r| r[0]),
|
||||
)?;
|
||||
|
||||
let p_mid: Option<Vec<_>> = m
|
||||
let p_mid: Value<Vec<_>> = m
|
||||
.iter()
|
||||
.map(|m_i| {
|
||||
r.as_ref().map(|r| {
|
||||
@@ -504,14 +502,14 @@ impl<F: FieldExt, const WIDTH: usize> Pow5State<F, WIDTH> {
|
||||
|| format!("round_{} rc_{}", round + 1, i),
|
||||
config.rc_b[i],
|
||||
offset,
|
||||
|| Ok(config.round_constants[round + 1][i]),
|
||||
|| Value::known(config.round_constants[round + 1][i]),
|
||||
)
|
||||
};
|
||||
for i in 0..WIDTH {
|
||||
load_round_constant(i)?;
|
||||
}
|
||||
|
||||
let r_mid: Option<Vec<_>> = p_mid.map(|p| {
|
||||
let r_mid: Value<Vec<_>> = p_mid.map(|p| {
|
||||
let r_0 = (p[0] + config.round_constants[round + 1][0]).pow(&config.alpha);
|
||||
let r_i = p[1..]
|
||||
.iter()
|
||||
@@ -520,7 +518,7 @@ impl<F: FieldExt, const WIDTH: usize> Pow5State<F, WIDTH> {
|
||||
std::iter::empty().chain(Some(r_0)).chain(r_i).collect()
|
||||
});
|
||||
|
||||
let state: Vec<Option<_>> = m
|
||||
let state: Vec<Value<_>> = m
|
||||
.iter()
|
||||
.map(|m_i| {
|
||||
r_mid.as_ref().map(|r| {
|
||||
@@ -557,7 +555,7 @@ impl<F: FieldExt, const WIDTH: usize> Pow5State<F, WIDTH> {
|
||||
round: usize,
|
||||
offset: usize,
|
||||
round_gate: Selector,
|
||||
round_fn: impl FnOnce(&mut Region<F>) -> Result<(usize, [Option<F>; WIDTH]), Error>,
|
||||
round_fn: impl FnOnce(&mut Region<F>) -> Result<(usize, [Value<F>; WIDTH]), Error>,
|
||||
) -> Result<Self, Error> {
|
||||
// Enable the required gate.
|
||||
round_gate.enable(region, offset)?;
|
||||
@@ -568,7 +566,7 @@ impl<F: FieldExt, const WIDTH: usize> Pow5State<F, WIDTH> {
|
||||
|| format!("round_{} rc_{}", round, i),
|
||||
config.rc_a[i],
|
||||
offset,
|
||||
|| Ok(config.round_constants[round][i]),
|
||||
|| Value::known(config.round_constants[round][i]),
|
||||
)
|
||||
};
|
||||
for i in 0..WIDTH {
|
||||
@@ -584,7 +582,7 @@ impl<F: FieldExt, const WIDTH: usize> Pow5State<F, WIDTH> {
|
||||
|| format!("round_{} state_{}", next_round, i),
|
||||
config.state[i],
|
||||
offset + 1,
|
||||
|| value.ok_or(Error::Synthesis),
|
||||
|| value,
|
||||
)?;
|
||||
Ok(StateWord(var))
|
||||
};
|
||||
@@ -598,7 +596,7 @@ impl<F: FieldExt, const WIDTH: usize> Pow5State<F, WIDTH> {
|
||||
mod tests {
|
||||
use group::ff::{Field, PrimeField};
|
||||
use halo2_proofs::{
|
||||
circuit::{Layouter, SimpleFloorPlanner},
|
||||
circuit::{Layouter, SimpleFloorPlanner, Value},
|
||||
dev::MockProver,
|
||||
pasta::Fp,
|
||||
plonk::{Circuit, ConstraintSystem, Error},
|
||||
@@ -653,12 +651,12 @@ mod tests {
|
||||
|| "prepare initial state",
|
||||
|mut region| {
|
||||
let state_word = |i: usize| {
|
||||
let value = Some(Fp::from(i as u64));
|
||||
let value = Value::known(Fp::from(i as u64));
|
||||
let var = region.assign_advice(
|
||||
|| format!("load state_{}", i),
|
||||
config.state[i],
|
||||
0,
|
||||
|| value.ok_or(Error::Synthesis),
|
||||
|| value,
|
||||
)?;
|
||||
Ok(StateWord(var))
|
||||
};
|
||||
@@ -697,7 +695,7 @@ mod tests {
|
||||
|| format!("load final_state_{}", i),
|
||||
config.state[i],
|
||||
0,
|
||||
|| Ok(expected_final_state[i]),
|
||||
|| Value::known(expected_final_state[i]),
|
||||
)?;
|
||||
region.constrain_equal(final_state[i].0.cell(), var.cell())
|
||||
};
|
||||
@@ -726,10 +724,10 @@ mod tests {
|
||||
const RATE: usize,
|
||||
const L: usize,
|
||||
> {
|
||||
message: Option<[Fp; L]>,
|
||||
message: Value<[Fp; L]>,
|
||||
// For the purpose of this test, witness the result.
|
||||
// TODO: Move this into an instance column.
|
||||
output: Option<Fp>,
|
||||
output: Value<Fp>,
|
||||
_spec: PhantomData<S>,
|
||||
}
|
||||
|
||||
@@ -741,8 +739,8 @@ mod tests {
|
||||
|
||||
fn without_witnesses(&self) -> Self {
|
||||
Self {
|
||||
message: None,
|
||||
output: None,
|
||||
message: Value::unknown(),
|
||||
output: Value::unknown(),
|
||||
_spec: PhantomData,
|
||||
}
|
||||
}
|
||||
@@ -781,7 +779,7 @@ mod tests {
|
||||
|| format!("load message_{}", i),
|
||||
config.state[i],
|
||||
0,
|
||||
|| value.ok_or(Error::Synthesis),
|
||||
|| value,
|
||||
)
|
||||
};
|
||||
|
||||
@@ -803,7 +801,7 @@ mod tests {
|
||||
|| "load output",
|
||||
config.state[0],
|
||||
0,
|
||||
|| self.output.ok_or(Error::Synthesis),
|
||||
|| self.output,
|
||||
)?;
|
||||
region.constrain_equal(output.cell(), expected_var.cell())
|
||||
},
|
||||
@@ -821,8 +819,8 @@ mod tests {
|
||||
|
||||
let k = 6;
|
||||
let circuit = HashCircuit::<OrchardNullifier, 3, 2, 2> {
|
||||
message: Some(message),
|
||||
output: Some(output),
|
||||
message: Value::known(message),
|
||||
output: Value::known(output),
|
||||
_spec: PhantomData,
|
||||
};
|
||||
let prover = MockProver::run(k, &circuit, vec![]).unwrap();
|
||||
@@ -839,8 +837,8 @@ mod tests {
|
||||
|
||||
let k = 7;
|
||||
let circuit = HashCircuit::<OrchardNullifier, 3, 2, 3> {
|
||||
message: Some(message),
|
||||
output: Some(output),
|
||||
message: Value::known(message),
|
||||
output: Value::known(output),
|
||||
_spec: PhantomData,
|
||||
};
|
||||
let prover = MockProver::run(k, &circuit, vec![]).unwrap();
|
||||
@@ -859,8 +857,8 @@ mod tests {
|
||||
|
||||
let k = 6;
|
||||
let circuit = HashCircuit::<OrchardNullifier, 3, 2, 2> {
|
||||
message: Some(message),
|
||||
output: Some(output),
|
||||
message: Value::known(message),
|
||||
output: Value::known(output),
|
||||
_spec: PhantomData,
|
||||
};
|
||||
let prover = MockProver::run(k, &circuit, vec![]).unwrap();
|
||||
@@ -880,8 +878,8 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
let circuit = HashCircuit::<OrchardNullifier, 3, 2, 2> {
|
||||
message: None,
|
||||
output: None,
|
||||
message: Value::unknown(),
|
||||
output: Value::unknown(),
|
||||
_spec: PhantomData,
|
||||
};
|
||||
halo2_proofs::dev::CircuitLayout::default()
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::marker::PhantomData;
|
||||
|
||||
use super::Sha256Instructions;
|
||||
use halo2_proofs::{
|
||||
circuit::{AssignedCell, Chip, Layouter, Region},
|
||||
circuit::{AssignedCell, Chip, Layouter, Region, Value},
|
||||
pasta::pallas,
|
||||
plonk::{Advice, Any, Assigned, Column, ConstraintSystem, Error},
|
||||
};
|
||||
@@ -49,7 +49,7 @@ const IV: [u32; STATE] = [
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
/// A word in a `Table16` message block.
|
||||
// TODO: Make the internals of this struct private.
|
||||
pub struct BlockWord(pub Option<u32>);
|
||||
pub struct BlockWord(pub Value<u32>);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
/// Little-endian bits (up to 64 bits)
|
||||
@@ -129,26 +129,26 @@ impl<const LEN: usize> AssignedBits<LEN> {
|
||||
annotation: A,
|
||||
column: impl Into<Column<Any>>,
|
||||
offset: usize,
|
||||
value: Option<T>,
|
||||
value: Value<T>,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
A: Fn() -> AR,
|
||||
AR: Into<String>,
|
||||
<T as TryInto<[bool; LEN]>>::Error: std::fmt::Debug,
|
||||
{
|
||||
let value: Option<[bool; LEN]> = value.map(|v| v.try_into().unwrap());
|
||||
let value: Option<Bits<LEN>> = value.map(|v| v.into());
|
||||
let value: Value<[bool; LEN]> = value.map(|v| v.try_into().unwrap());
|
||||
let value: Value<Bits<LEN>> = value.map(|v| v.into());
|
||||
|
||||
let column: Column<Any> = column.into();
|
||||
match column.column_type() {
|
||||
Any::Advice => {
|
||||
region.assign_advice(annotation, column.try_into().unwrap(), offset, || {
|
||||
value.clone().ok_or(Error::Synthesis)
|
||||
value.clone()
|
||||
})
|
||||
}
|
||||
Any::Fixed => {
|
||||
region.assign_fixed(annotation, column.try_into().unwrap(), offset, || {
|
||||
value.clone().ok_or(Error::Synthesis)
|
||||
value.clone()
|
||||
})
|
||||
}
|
||||
_ => panic!("Cannot assign to instance column"),
|
||||
@@ -158,7 +158,7 @@ impl<const LEN: usize> AssignedBits<LEN> {
|
||||
}
|
||||
|
||||
impl AssignedBits<16> {
|
||||
fn value_u16(&self) -> Option<u16> {
|
||||
fn value_u16(&self) -> Value<u16> {
|
||||
self.value().map(|v| v.into())
|
||||
}
|
||||
|
||||
@@ -167,23 +167,23 @@ impl AssignedBits<16> {
|
||||
annotation: A,
|
||||
column: impl Into<Column<Any>>,
|
||||
offset: usize,
|
||||
value: Option<u16>,
|
||||
value: Value<u16>,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
A: Fn() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
let column: Column<Any> = column.into();
|
||||
let value: Option<Bits<16>> = value.map(|v| v.into());
|
||||
let value: Value<Bits<16>> = value.map(|v| v.into());
|
||||
match column.column_type() {
|
||||
Any::Advice => {
|
||||
region.assign_advice(annotation, column.try_into().unwrap(), offset, || {
|
||||
value.clone().ok_or(Error::Synthesis)
|
||||
value.clone()
|
||||
})
|
||||
}
|
||||
Any::Fixed => {
|
||||
region.assign_fixed(annotation, column.try_into().unwrap(), offset, || {
|
||||
value.clone().ok_or(Error::Synthesis)
|
||||
value.clone()
|
||||
})
|
||||
}
|
||||
_ => panic!("Cannot assign to instance column"),
|
||||
@@ -193,7 +193,7 @@ impl AssignedBits<16> {
|
||||
}
|
||||
|
||||
impl AssignedBits<32> {
|
||||
fn value_u32(&self) -> Option<u32> {
|
||||
fn value_u32(&self) -> Value<u32> {
|
||||
self.value().map(|v| v.into())
|
||||
}
|
||||
|
||||
@@ -202,23 +202,23 @@ impl AssignedBits<32> {
|
||||
annotation: A,
|
||||
column: impl Into<Column<Any>>,
|
||||
offset: usize,
|
||||
value: Option<u32>,
|
||||
value: Value<u32>,
|
||||
) -> Result<Self, Error>
|
||||
where
|
||||
A: Fn() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
let column: Column<Any> = column.into();
|
||||
let value: Option<Bits<32>> = value.map(|v| v.into());
|
||||
let value: Value<Bits<32>> = value.map(|v| v.into());
|
||||
match column.column_type() {
|
||||
Any::Advice => {
|
||||
region.assign_advice(annotation, column.try_into().unwrap(), offset, || {
|
||||
value.clone().ok_or(Error::Synthesis)
|
||||
value.clone()
|
||||
})
|
||||
}
|
||||
Any::Fixed => {
|
||||
region.assign_fixed(annotation, column.try_into().unwrap(), offset, || {
|
||||
value.clone().ok_or(Error::Synthesis)
|
||||
value.clone()
|
||||
})
|
||||
}
|
||||
_ => panic!("Cannot assign to instance column"),
|
||||
@@ -384,10 +384,10 @@ trait Table16Assignment {
|
||||
lookup: &SpreadInputs,
|
||||
a_3: Column<Advice>,
|
||||
row: usize,
|
||||
r_0_even: Option<[bool; 16]>,
|
||||
r_0_odd: Option<[bool; 16]>,
|
||||
r_1_even: Option<[bool; 16]>,
|
||||
r_1_odd: Option<[bool; 16]>,
|
||||
r_0_even: Value<[bool; 16]>,
|
||||
r_0_odd: Value<[bool; 16]>,
|
||||
r_1_even: Value<[bool; 16]>,
|
||||
r_1_odd: Value<[bool; 16]>,
|
||||
) -> Result<
|
||||
(
|
||||
(AssignedBits<16>, AssignedBits<16>),
|
||||
@@ -436,10 +436,10 @@ trait Table16Assignment {
|
||||
lookup: &SpreadInputs,
|
||||
a_3: Column<Advice>,
|
||||
row: usize,
|
||||
r_0_even: Option<[bool; 16]>,
|
||||
r_0_odd: Option<[bool; 16]>,
|
||||
r_1_even: Option<[bool; 16]>,
|
||||
r_1_odd: Option<[bool; 16]>,
|
||||
r_0_even: Value<[bool; 16]>,
|
||||
r_0_odd: Value<[bool; 16]>,
|
||||
r_1_even: Value<[bool; 16]>,
|
||||
r_1_odd: Value<[bool; 16]>,
|
||||
) -> Result<(AssignedBits<16>, AssignedBits<16>), Error> {
|
||||
let (even, _odd) = self.assign_spread_outputs(
|
||||
region, lookup, a_3, row, r_0_even, r_0_odd, r_1_even, r_1_odd,
|
||||
|
||||
@@ -4,7 +4,7 @@ use super::{
|
||||
AssignedBits, BlockWord, SpreadInputs, SpreadVar, Table16Assignment, ROUNDS, STATE,
|
||||
};
|
||||
use halo2_proofs::{
|
||||
circuit::Layouter,
|
||||
circuit::{Layouter, Value},
|
||||
pasta::pallas,
|
||||
plonk::{Advice, Column, ConstraintSystem, Error, Selector},
|
||||
poly::Rotation,
|
||||
@@ -27,12 +27,12 @@ pub trait UpperSigmaVar<
|
||||
const D_LEN: usize,
|
||||
>
|
||||
{
|
||||
fn spread_a(&self) -> Option<[bool; A_LEN]>;
|
||||
fn spread_b(&self) -> Option<[bool; B_LEN]>;
|
||||
fn spread_c(&self) -> Option<[bool; C_LEN]>;
|
||||
fn spread_d(&self) -> Option<[bool; D_LEN]>;
|
||||
fn spread_a(&self) -> Value<[bool; A_LEN]>;
|
||||
fn spread_b(&self) -> Value<[bool; B_LEN]>;
|
||||
fn spread_c(&self) -> Value<[bool; C_LEN]>;
|
||||
fn spread_d(&self) -> Value<[bool; D_LEN]>;
|
||||
|
||||
fn xor_upper_sigma(&self) -> Option<[bool; 64]> {
|
||||
fn xor_upper_sigma(&self) -> Value<[bool; 64]> {
|
||||
self.spread_a()
|
||||
.zip(self.spread_b())
|
||||
.zip(self.spread_c())
|
||||
@@ -128,15 +128,15 @@ impl AbcdVar {
|
||||
}
|
||||
|
||||
impl UpperSigmaVar<4, 22, 18, 20> for AbcdVar {
|
||||
fn spread_a(&self) -> Option<[bool; 4]> {
|
||||
fn spread_a(&self) -> Value<[bool; 4]> {
|
||||
self.a.spread.value().map(|v| v.0)
|
||||
}
|
||||
|
||||
fn spread_b(&self) -> Option<[bool; 22]> {
|
||||
fn spread_b(&self) -> Value<[bool; 22]> {
|
||||
self.b.spread.value().map(|v| v.0)
|
||||
}
|
||||
|
||||
fn spread_c(&self) -> Option<[bool; 18]> {
|
||||
fn spread_c(&self) -> Value<[bool; 18]> {
|
||||
self.c_lo
|
||||
.spread
|
||||
.value()
|
||||
@@ -153,7 +153,7 @@ impl UpperSigmaVar<4, 22, 18, 20> for AbcdVar {
|
||||
})
|
||||
}
|
||||
|
||||
fn spread_d(&self) -> Option<[bool; 20]> {
|
||||
fn spread_d(&self) -> Value<[bool; 20]> {
|
||||
self.d.spread.value().map(|v| v.0)
|
||||
}
|
||||
}
|
||||
@@ -217,7 +217,7 @@ impl EfghVar {
|
||||
}
|
||||
|
||||
impl UpperSigmaVar<12, 10, 28, 14> for EfghVar {
|
||||
fn spread_a(&self) -> Option<[bool; 12]> {
|
||||
fn spread_a(&self) -> Value<[bool; 12]> {
|
||||
self.a_lo
|
||||
.spread
|
||||
.value()
|
||||
@@ -232,7 +232,7 @@ impl UpperSigmaVar<12, 10, 28, 14> for EfghVar {
|
||||
})
|
||||
}
|
||||
|
||||
fn spread_b(&self) -> Option<[bool; 10]> {
|
||||
fn spread_b(&self) -> Value<[bool; 10]> {
|
||||
self.b_lo
|
||||
.spread
|
||||
.value()
|
||||
@@ -247,11 +247,11 @@ impl UpperSigmaVar<12, 10, 28, 14> for EfghVar {
|
||||
})
|
||||
}
|
||||
|
||||
fn spread_c(&self) -> Option<[bool; 28]> {
|
||||
fn spread_c(&self) -> Value<[bool; 28]> {
|
||||
self.c.spread.value().map(|v| v.0)
|
||||
}
|
||||
|
||||
fn spread_d(&self) -> Option<[bool; 14]> {
|
||||
fn spread_d(&self) -> Value<[bool; 14]> {
|
||||
self.d.spread.value().map(|v| v.0)
|
||||
}
|
||||
}
|
||||
@@ -266,7 +266,7 @@ impl From<(AssignedBits<16>, AssignedBits<16>)> for RoundWordDense {
|
||||
}
|
||||
|
||||
impl RoundWordDense {
|
||||
pub fn value(&self) -> Option<u32> {
|
||||
pub fn value(&self) -> Value<u32> {
|
||||
self.0
|
||||
.value_u16()
|
||||
.zip(self.1.value_u16())
|
||||
@@ -284,7 +284,7 @@ impl From<(AssignedBits<32>, AssignedBits<32>)> for RoundWordSpread {
|
||||
}
|
||||
|
||||
impl RoundWordSpread {
|
||||
pub fn value(&self) -> Option<u64> {
|
||||
pub fn value(&self) -> Value<u64> {
|
||||
self.0
|
||||
.value_u32()
|
||||
.zip(self.1.value_u32())
|
||||
@@ -922,7 +922,7 @@ impl CompressionConfig {
|
||||
layouter: &mut impl Layouter<pallas::Base>,
|
||||
state: State,
|
||||
) -> Result<[BlockWord; DIGEST_SIZE], Error> {
|
||||
let mut digest = [BlockWord(Some(0)); DIGEST_SIZE];
|
||||
let mut digest = [BlockWord(Value::known(0)); DIGEST_SIZE];
|
||||
layouter.assign_region(
|
||||
|| "digest",
|
||||
|mut region| {
|
||||
@@ -984,10 +984,10 @@ mod tests {
|
||||
|
||||
let digest = config.compression.digest(&mut layouter, state)?;
|
||||
for (idx, digest_word) in digest.iter().enumerate() {
|
||||
assert_eq!(
|
||||
(digest_word.0.unwrap() as u64 + IV[idx] as u64) as u32,
|
||||
super::compression_util::COMPRESSION_OUTPUT[idx]
|
||||
);
|
||||
digest_word.0.assert_if_known(|digest_word| {
|
||||
(*digest_word as u64 + IV[idx] as u64) as u32
|
||||
== super::compression_util::COMPRESSION_OUTPUT[idx]
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::sha256::table16::{
|
||||
util::*, AssignedBits, SpreadVar, SpreadWord, StateWord, Table16Assignment,
|
||||
};
|
||||
use halo2_proofs::{
|
||||
circuit::Region,
|
||||
circuit::{Region, Value},
|
||||
pasta::pallas,
|
||||
plonk::{Advice, Column, Error},
|
||||
};
|
||||
@@ -203,7 +203,7 @@ impl CompressionConfig {
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
row: usize,
|
||||
val: Option<u32>,
|
||||
val: Value<u32>,
|
||||
) -> Result<AbcdVar, Error> {
|
||||
self.s_decompose_abcd.enable(region, row)?;
|
||||
|
||||
@@ -213,7 +213,7 @@ impl CompressionConfig {
|
||||
let a_6 = self.extras[2];
|
||||
|
||||
let spread_pieces = val.map(AbcdVar::pieces);
|
||||
let spread_pieces = transpose_option_vec(spread_pieces, 6);
|
||||
let spread_pieces = spread_pieces.transpose_vec(6);
|
||||
|
||||
let a = SpreadVar::without_lookup(
|
||||
region,
|
||||
@@ -274,7 +274,7 @@ impl CompressionConfig {
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
row: usize,
|
||||
val: Option<u32>,
|
||||
val: Value<u32>,
|
||||
) -> Result<EfghVar, Error> {
|
||||
self.s_decompose_efgh.enable(region, row)?;
|
||||
|
||||
@@ -284,7 +284,7 @@ impl CompressionConfig {
|
||||
let a_6 = self.extras[2];
|
||||
|
||||
let spread_pieces = val.map(EfghVar::pieces);
|
||||
let spread_pieces = transpose_option_vec(spread_pieces, 6);
|
||||
let spread_pieces = spread_pieces.transpose_vec(6);
|
||||
|
||||
let a_lo = SpreadVar::without_lookup(
|
||||
region,
|
||||
@@ -345,7 +345,7 @@ impl CompressionConfig {
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
round_idx: RoundIdx,
|
||||
a_val: Option<u32>,
|
||||
a_val: Value<u32>,
|
||||
) -> Result<RoundWordA, Error> {
|
||||
let row = get_decompose_a_row(round_idx);
|
||||
|
||||
@@ -358,7 +358,7 @@ impl CompressionConfig {
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
round_idx: RoundIdx,
|
||||
e_val: Option<u32>,
|
||||
e_val: Value<u32>,
|
||||
) -> Result<RoundWordE, Error> {
|
||||
let row = get_decompose_e_row(round_idx);
|
||||
|
||||
@@ -405,11 +405,11 @@ impl CompressionConfig {
|
||||
|
||||
// Calculate R_0^{even}, R_0^{odd}, R_1^{even}, R_1^{odd}
|
||||
let r = word.xor_upper_sigma();
|
||||
let r_0: Option<[bool; 32]> = r.map(|r| r[..32].try_into().unwrap());
|
||||
let r_0: Value<[bool; 32]> = r.map(|r| r[..32].try_into().unwrap());
|
||||
let r_0_even = r_0.map(even_bits);
|
||||
let r_0_odd = r_0.map(odd_bits);
|
||||
|
||||
let r_1: Option<[bool; 32]> = r.map(|r| r[32..].try_into().unwrap());
|
||||
let r_1: Value<[bool; 32]> = r.map(|r| r[32..].try_into().unwrap());
|
||||
let r_1_even = r_1.map(even_bits);
|
||||
let r_1_odd = r_1.map(odd_bits);
|
||||
|
||||
@@ -464,11 +464,11 @@ impl CompressionConfig {
|
||||
// Calculate R_0^{even}, R_0^{odd}, R_1^{even}, R_1^{odd}
|
||||
// Calculate R_0^{even}, R_0^{odd}, R_1^{even}, R_1^{odd}
|
||||
let r = word.xor_upper_sigma();
|
||||
let r_0: Option<[bool; 32]> = r.map(|r| r[..32].try_into().unwrap());
|
||||
let r_0: Value<[bool; 32]> = r.map(|r| r[..32].try_into().unwrap());
|
||||
let r_0_even = r_0.map(even_bits);
|
||||
let r_0_odd = r_0.map(odd_bits);
|
||||
|
||||
let r_1: Option<[bool; 32]> = r.map(|r| r[32..].try_into().unwrap());
|
||||
let r_1: Value<[bool; 32]> = r.map(|r| r[32..].try_into().unwrap());
|
||||
let r_1_even = r_1.map(even_bits);
|
||||
let r_1_odd = r_1.map(odd_bits);
|
||||
|
||||
@@ -488,10 +488,10 @@ impl CompressionConfig {
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
row: usize,
|
||||
r_0_even: Option<[bool; 16]>,
|
||||
r_0_odd: Option<[bool; 16]>,
|
||||
r_1_even: Option<[bool; 16]>,
|
||||
r_1_odd: Option<[bool; 16]>,
|
||||
r_0_even: Value<[bool; 16]>,
|
||||
r_0_odd: Value<[bool; 16]>,
|
||||
r_1_even: Value<[bool; 16]>,
|
||||
r_1_odd: Value<[bool; 16]>,
|
||||
) -> Result<(AssignedBits<16>, AssignedBits<16>), Error> {
|
||||
let a_3 = self.extras[0];
|
||||
|
||||
@@ -539,16 +539,16 @@ impl CompressionConfig {
|
||||
.1
|
||||
.copy_advice(|| "spread_f_hi", region, a_4, row + 1)?;
|
||||
|
||||
let p: Option<[bool; 64]> = spread_halves_e
|
||||
let p: Value<[bool; 64]> = spread_halves_e
|
||||
.value()
|
||||
.zip(spread_halves_f.value())
|
||||
.map(|(e, f)| i2lebsp(e + f));
|
||||
|
||||
let p_0: Option<[bool; 32]> = p.map(|p| p[..32].try_into().unwrap());
|
||||
let p_0: Value<[bool; 32]> = p.map(|p| p[..32].try_into().unwrap());
|
||||
let p_0_even = p_0.map(even_bits);
|
||||
let p_0_odd = p_0.map(odd_bits);
|
||||
|
||||
let p_1: Option<[bool; 32]> = p.map(|p| p[32..].try_into().unwrap());
|
||||
let p_1: Value<[bool; 32]> = p.map(|p| p[32..].try_into().unwrap());
|
||||
let p_1_even = p_1.map(even_bits);
|
||||
let p_1_odd = p_1.map(odd_bits);
|
||||
|
||||
@@ -614,7 +614,7 @@ impl CompressionConfig {
|
||||
spread_neg_e_hi,
|
||||
)?;
|
||||
|
||||
let p: Option<[bool; 64]> = {
|
||||
let p: Value<[bool; 64]> = {
|
||||
let spread_neg_e = spread_neg_e_lo
|
||||
.zip(spread_neg_e_hi)
|
||||
.map(|(lo, hi)| lebs2ip(&lo) + (1 << 32) * lebs2ip(&hi));
|
||||
@@ -623,11 +623,11 @@ impl CompressionConfig {
|
||||
.map(|(neg_e, g)| i2lebsp(neg_e + g))
|
||||
};
|
||||
|
||||
let p_0: Option<[bool; 32]> = p.map(|p| p[..32].try_into().unwrap());
|
||||
let p_0: Value<[bool; 32]> = p.map(|p| p[..32].try_into().unwrap());
|
||||
let p_0_even = p_0.map(even_bits);
|
||||
let p_0_odd = p_0.map(odd_bits);
|
||||
|
||||
let p_1: Option<[bool; 32]> = p.map(|p| p[32..].try_into().unwrap());
|
||||
let p_1: Value<[bool; 32]> = p.map(|p| p[32..].try_into().unwrap());
|
||||
let p_1_even = p_1.map(even_bits);
|
||||
let p_1_odd = p_1.map(odd_bits);
|
||||
|
||||
@@ -638,10 +638,10 @@ impl CompressionConfig {
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
row: usize,
|
||||
r_0_even: Option<[bool; 16]>,
|
||||
r_0_odd: Option<[bool; 16]>,
|
||||
r_1_even: Option<[bool; 16]>,
|
||||
r_1_odd: Option<[bool; 16]>,
|
||||
r_0_even: Value<[bool; 16]>,
|
||||
r_0_odd: Value<[bool; 16]>,
|
||||
r_1_even: Value<[bool; 16]>,
|
||||
r_1_odd: Value<[bool; 16]>,
|
||||
) -> Result<(AssignedBits<16>, AssignedBits<16>), Error> {
|
||||
let a_3 = self.extras[0];
|
||||
let (_even, odd) = self.assign_spread_outputs(
|
||||
@@ -697,17 +697,17 @@ impl CompressionConfig {
|
||||
.1
|
||||
.copy_advice(|| "spread_c_hi", region, a_5, row + 1)?;
|
||||
|
||||
let m: Option<[bool; 64]> = spread_halves_a
|
||||
let m: Value<[bool; 64]> = spread_halves_a
|
||||
.value()
|
||||
.zip(spread_halves_b.value())
|
||||
.zip(spread_halves_c.value())
|
||||
.map(|((a, b), c)| i2lebsp(a + b + c));
|
||||
|
||||
let m_0: Option<[bool; 32]> = m.map(|m| m[..32].try_into().unwrap());
|
||||
let m_0: Value<[bool; 32]> = m.map(|m| m[..32].try_into().unwrap());
|
||||
let m_0_even = m_0.map(even_bits);
|
||||
let m_0_odd = m_0.map(odd_bits);
|
||||
|
||||
let m_1: Option<[bool; 32]> = m.map(|m| m[32..].try_into().unwrap());
|
||||
let m_1: Value<[bool; 32]> = m.map(|m| m[32..].try_into().unwrap());
|
||||
let m_1_even = m_1.map(even_bits);
|
||||
let m_1_odd = m_1.map(odd_bits);
|
||||
|
||||
@@ -750,8 +750,8 @@ impl CompressionConfig {
|
||||
let k_lo: [bool; 16] = k[..16].try_into().unwrap();
|
||||
let k_hi: [bool; 16] = k[16..].try_into().unwrap();
|
||||
{
|
||||
AssignedBits::<16>::assign_bits(region, || "k_lo", a_6, row - 1, Some(k_lo))?;
|
||||
AssignedBits::<16>::assign_bits(region, || "k_hi", a_6, row, Some(k_hi))?;
|
||||
AssignedBits::<16>::assign_bits(region, || "k_lo", a_6, row - 1, Value::known(k_lo))?;
|
||||
AssignedBits::<16>::assign_bits(region, || "k_hi", a_6, row, Value::known(k_hi))?;
|
||||
}
|
||||
|
||||
// Assign and copy w
|
||||
@@ -772,7 +772,10 @@ impl CompressionConfig {
|
||||
(ch.0.value_u16(), ch.1.value_u16()),
|
||||
(ch_neg.0.value_u16(), ch_neg.1.value_u16()),
|
||||
(sigma_1.0.value_u16(), sigma_1.1.value_u16()),
|
||||
(Some(lebs2ip(&k_lo) as u16), Some(lebs2ip(&k_hi) as u16)),
|
||||
(
|
||||
Value::known(lebs2ip(&k_lo) as u16),
|
||||
Value::known(lebs2ip(&k_hi) as u16),
|
||||
),
|
||||
(w.0.value_u16(), w.1.value_u16()),
|
||||
]);
|
||||
|
||||
@@ -780,16 +783,12 @@ impl CompressionConfig {
|
||||
|| "h_prime_carry",
|
||||
a_9,
|
||||
row + 1,
|
||||
|| {
|
||||
h_prime_carry
|
||||
.map(|value| pallas::Base::from(value as u64))
|
||||
.ok_or(Error::Synthesis)
|
||||
},
|
||||
|| h_prime_carry.map(|value| pallas::Base::from(value as u64)),
|
||||
)?;
|
||||
|
||||
let h_prime: Option<[bool; 32]> = h_prime.map(|w| i2lebsp(w.into()));
|
||||
let h_prime_lo: Option<[bool; 16]> = h_prime.map(|w| w[..16].try_into().unwrap());
|
||||
let h_prime_hi: Option<[bool; 16]> = h_prime.map(|w| w[16..].try_into().unwrap());
|
||||
let h_prime: Value<[bool; 32]> = h_prime.map(|w| i2lebsp(w.into()));
|
||||
let h_prime_lo: Value<[bool; 16]> = h_prime.map(|w| w[..16].try_into().unwrap());
|
||||
let h_prime_hi: Value<[bool; 16]> = h_prime.map(|w| w[16..].try_into().unwrap());
|
||||
|
||||
let h_prime_lo =
|
||||
AssignedBits::<16>::assign_bits(region, || "h_prime_lo", a_7, row + 1, h_prime_lo)?;
|
||||
@@ -831,7 +830,7 @@ impl CompressionConfig {
|
||||
|| "e_new_carry",
|
||||
a_9,
|
||||
row + 1,
|
||||
|| e_new_carry.map(pallas::Base::from).ok_or(Error::Synthesis),
|
||||
|| e_new_carry.map(pallas::Base::from),
|
||||
)?;
|
||||
|
||||
Ok(e_new_dense)
|
||||
@@ -885,7 +884,7 @@ impl CompressionConfig {
|
||||
|| "a_new_carry",
|
||||
a_9,
|
||||
row,
|
||||
|| a_new_carry.map(pallas::Base::from).ok_or(Error::Synthesis),
|
||||
|| a_new_carry.map(pallas::Base::from),
|
||||
)?;
|
||||
|
||||
Ok(a_new_dense)
|
||||
@@ -898,17 +897,17 @@ impl CompressionConfig {
|
||||
lo_col: Column<Advice>,
|
||||
hi_row: usize,
|
||||
hi_col: Column<Advice>,
|
||||
word: Option<u32>,
|
||||
word: Value<u32>,
|
||||
) -> Result<RoundWordDense, Error> {
|
||||
let word: Option<[bool; 32]> = word.map(|w| i2lebsp(w.into()));
|
||||
let word: Value<[bool; 32]> = word.map(|w| i2lebsp(w.into()));
|
||||
|
||||
let lo = {
|
||||
let lo: Option<[bool; 16]> = word.map(|w| w[..16].try_into().unwrap());
|
||||
let lo: Value<[bool; 16]> = word.map(|w| w[..16].try_into().unwrap());
|
||||
AssignedBits::<16>::assign_bits(region, || "lo", lo_col, lo_row, lo)?
|
||||
};
|
||||
|
||||
let hi = {
|
||||
let hi: Option<[bool; 16]> = word.map(|w| w[16..].try_into().unwrap());
|
||||
let hi: Value<[bool; 16]> = word.map(|w| w[16..].try_into().unwrap());
|
||||
AssignedBits::<16>::assign_bits(region, || "hi", hi_col, hi_row, hi)?
|
||||
};
|
||||
|
||||
@@ -921,15 +920,15 @@ impl CompressionConfig {
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
row: usize,
|
||||
word: Option<u32>,
|
||||
word: Value<u32>,
|
||||
) -> Result<(RoundWordDense, RoundWordSpread), Error> {
|
||||
// Rename these here for ease of matching the gates to the specification.
|
||||
let a_7 = self.extras[3];
|
||||
let a_8 = self.extras[4];
|
||||
|
||||
let word: Option<[bool; 32]> = word.map(|w| i2lebsp(w.into()));
|
||||
let lo: Option<[bool; 16]> = word.map(|w| w[..16].try_into().unwrap());
|
||||
let hi: Option<[bool; 16]> = word.map(|w| w[16..].try_into().unwrap());
|
||||
let word: Value<[bool; 32]> = word.map(|w| i2lebsp(w.into()));
|
||||
let lo: Value<[bool; 16]> = word.map(|w| w[..16].try_into().unwrap());
|
||||
let hi: Value<[bool; 16]> = word.map(|w| w[16..].try_into().unwrap());
|
||||
|
||||
let w_lo = SpreadVar::without_lookup(region, a_7, row, a_8, row, lo.map(SpreadWord::new))?;
|
||||
let w_hi =
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::super::{super::DIGEST_SIZE, BlockWord, RoundWordDense};
|
||||
use super::{compression_util::*, CompressionConfig, State};
|
||||
use halo2_proofs::{
|
||||
circuit::Region,
|
||||
circuit::{Region, Value},
|
||||
pasta::pallas,
|
||||
plonk::{Advice, Column, Error},
|
||||
};
|
||||
@@ -39,10 +39,7 @@ impl CompressionConfig {
|
||||
|| "a",
|
||||
a_5,
|
||||
abcd_row,
|
||||
|| {
|
||||
a.map(|a| pallas::Base::from(a as u64))
|
||||
.ok_or(Error::Synthesis)
|
||||
},
|
||||
|| a.map(|a| pallas::Base::from(a as u64)),
|
||||
)?;
|
||||
|
||||
let b = self.assign_digest_word(region, abcd_row, a_6, a_7, a_8, b.dense_halves)?;
|
||||
@@ -61,10 +58,7 @@ impl CompressionConfig {
|
||||
|| "e",
|
||||
a_5,
|
||||
efgh_row,
|
||||
|| {
|
||||
e.map(|e| pallas::Base::from(e as u64))
|
||||
.ok_or(Error::Synthesis)
|
||||
},
|
||||
|| e.map(|e| pallas::Base::from(e as u64)),
|
||||
)?;
|
||||
|
||||
let f = self.assign_digest_word(region, efgh_row, a_6, a_7, a_8, f.dense_halves)?;
|
||||
@@ -91,7 +85,7 @@ impl CompressionConfig {
|
||||
hi_col: Column<Advice>,
|
||||
word_col: Column<Advice>,
|
||||
dense_halves: RoundWordDense,
|
||||
) -> Result<Option<u32>, Error> {
|
||||
) -> Result<Value<u32>, Error> {
|
||||
dense_halves.0.copy_advice(|| "lo", region, lo_col, row)?;
|
||||
dense_halves.1.copy_advice(|| "hi", region, hi_col, row)?;
|
||||
|
||||
@@ -100,10 +94,7 @@ impl CompressionConfig {
|
||||
|| "word",
|
||||
word_col,
|
||||
row,
|
||||
|| {
|
||||
val.map(|val| pallas::Base::from(val as u64))
|
||||
.ok_or(Error::Synthesis)
|
||||
},
|
||||
|| val.map(|val| pallas::Base::from(val as u64)),
|
||||
)?;
|
||||
|
||||
Ok(val)
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
use super::super::{RoundWord, StateWord, STATE};
|
||||
use super::{compression_util::*, CompressionConfig, State};
|
||||
use halo2_proofs::{circuit::Region, pasta::pallas, plonk::Error};
|
||||
use halo2_proofs::{
|
||||
circuit::{Region, Value},
|
||||
pasta::pallas,
|
||||
plonk::Error,
|
||||
};
|
||||
|
||||
impl CompressionConfig {
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
@@ -12,26 +16,28 @@ impl CompressionConfig {
|
||||
let a_7 = self.extras[3];
|
||||
|
||||
// Decompose E into (6, 5, 14, 7)-bit chunks
|
||||
let e = self.decompose_e(region, RoundIdx::Init, Some(iv[4]))?;
|
||||
let e = self.decompose_e(region, RoundIdx::Init, Value::known(iv[4]))?;
|
||||
|
||||
// Decompose F, G
|
||||
let f = self.decompose_f(region, InitialRound, Some(iv[5]))?;
|
||||
let g = self.decompose_g(region, InitialRound, Some(iv[6]))?;
|
||||
let f = self.decompose_f(region, InitialRound, Value::known(iv[5]))?;
|
||||
let g = self.decompose_g(region, InitialRound, Value::known(iv[6]))?;
|
||||
|
||||
// Assign H
|
||||
let h_row = get_h_row(RoundIdx::Init);
|
||||
let h = self.assign_word_halves_dense(region, h_row, a_7, h_row + 1, a_7, Some(iv[7]))?;
|
||||
let h =
|
||||
self.assign_word_halves_dense(region, h_row, a_7, h_row + 1, a_7, Value::known(iv[7]))?;
|
||||
|
||||
// Decompose A into (2, 11, 9, 10)-bit chunks
|
||||
let a = self.decompose_a(region, RoundIdx::Init, Some(iv[0]))?;
|
||||
let a = self.decompose_a(region, RoundIdx::Init, Value::known(iv[0]))?;
|
||||
|
||||
// Decompose B, C
|
||||
let b = self.decompose_b(region, InitialRound, Some(iv[1]))?;
|
||||
let c = self.decompose_c(region, InitialRound, Some(iv[2]))?;
|
||||
let b = self.decompose_b(region, InitialRound, Value::known(iv[1]))?;
|
||||
let c = self.decompose_c(region, InitialRound, Value::known(iv[2]))?;
|
||||
|
||||
// Assign D
|
||||
let d_row = get_d_row(RoundIdx::Init);
|
||||
let d = self.assign_word_halves_dense(region, d_row, a_7, d_row + 1, a_7, Some(iv[3]))?;
|
||||
let d =
|
||||
self.assign_word_halves_dense(region, d_row, a_7, d_row + 1, a_7, Value::known(iv[3]))?;
|
||||
|
||||
Ok(State::new(
|
||||
StateWord::A(a),
|
||||
@@ -100,7 +106,7 @@ impl CompressionConfig {
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
round_idx: InitialRound,
|
||||
b_val: Option<u32>,
|
||||
b_val: Value<u32>,
|
||||
) -> Result<RoundWord, Error> {
|
||||
let row = get_decompose_b_row(round_idx);
|
||||
|
||||
@@ -113,7 +119,7 @@ impl CompressionConfig {
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
round_idx: InitialRound,
|
||||
c_val: Option<u32>,
|
||||
c_val: Value<u32>,
|
||||
) -> Result<RoundWord, Error> {
|
||||
let row = get_decompose_c_row(round_idx);
|
||||
|
||||
@@ -126,7 +132,7 @@ impl CompressionConfig {
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
round_idx: InitialRound,
|
||||
f_val: Option<u32>,
|
||||
f_val: Value<u32>,
|
||||
) -> Result<RoundWord, Error> {
|
||||
let row = get_decompose_f_row(round_idx);
|
||||
|
||||
@@ -139,7 +145,7 @@ impl CompressionConfig {
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
round_idx: InitialRound,
|
||||
g_val: Option<u32>,
|
||||
g_val: Value<u32>,
|
||||
) -> Result<RoundWord, Error> {
|
||||
let row = get_decompose_g_row(round_idx);
|
||||
|
||||
|
||||
@@ -435,8 +435,10 @@ mod tests {
|
||||
// Run message_scheduler to get W_[0..64]
|
||||
let (w, _) = config.message_schedule.process(&mut layouter, inputs)?;
|
||||
for (word, test_word) in w.iter().zip(MSG_SCHEDULE_TEST_OUTPUT.iter()) {
|
||||
let word: u32 = lebs2ip(word.value().unwrap()) as u32;
|
||||
assert_eq!(word, *test_word);
|
||||
word.value().assert_if_known(|bits| {
|
||||
let word: u32 = lebs2ip(bits) as u32;
|
||||
word == *test_word
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
use super::super::AssignedBits;
|
||||
use super::MessageScheduleConfig;
|
||||
use halo2_proofs::{circuit::Region, pasta::pallas, plonk::Error};
|
||||
use halo2_proofs::{
|
||||
circuit::{Region, Value},
|
||||
pasta::pallas,
|
||||
plonk::Error,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
use super::super::{super::BLOCK_SIZE, BlockWord, ROUNDS};
|
||||
@@ -57,22 +61,22 @@ pub fn get_word_row(word_idx: usize) -> usize {
|
||||
#[cfg(test)]
|
||||
pub fn msg_schedule_test_input() -> [BlockWord; BLOCK_SIZE] {
|
||||
[
|
||||
BlockWord(Some(0b01100001011000100110001110000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000000000)),
|
||||
BlockWord(Some(0b00000000000000000000000000011000)),
|
||||
BlockWord(Value::known(0b01100001011000100110001110000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000000000)),
|
||||
BlockWord(Value::known(0b00000000000000000000000000011000)),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -149,7 +153,7 @@ impl MessageScheduleConfig {
|
||||
pub fn assign_word_and_halves(
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
word: Option<u32>,
|
||||
word: Value<u32>,
|
||||
word_idx: usize,
|
||||
) -> Result<(AssignedBits<32>, (AssignedBits<16>, AssignedBits<16>)), Error> {
|
||||
// Rename these here for ease of matching the gates to the specification.
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
use super::super::{util::*, AssignedBits, BlockWord, SpreadVar, SpreadWord, Table16Assignment};
|
||||
use super::{schedule_util::*, MessageScheduleConfig};
|
||||
use halo2_proofs::{circuit::Region, pasta::pallas, plonk::Error};
|
||||
use halo2_proofs::{
|
||||
circuit::{Region, Value},
|
||||
pasta::pallas,
|
||||
plonk::Error,
|
||||
};
|
||||
use std::convert::TryInto;
|
||||
|
||||
// A word in subregion 1
|
||||
@@ -17,23 +21,23 @@ pub struct Subregion1Word {
|
||||
}
|
||||
|
||||
impl Subregion1Word {
|
||||
fn spread_a(&self) -> Option<[bool; 6]> {
|
||||
fn spread_a(&self) -> Value<[bool; 6]> {
|
||||
self.a.value().map(|v| v.spread())
|
||||
}
|
||||
|
||||
fn spread_b(&self) -> Option<[bool; 8]> {
|
||||
fn spread_b(&self) -> Value<[bool; 8]> {
|
||||
self.b.value().map(|v| v.spread())
|
||||
}
|
||||
|
||||
fn spread_c(&self) -> Option<[bool; 22]> {
|
||||
fn spread_c(&self) -> Value<[bool; 22]> {
|
||||
self.spread_c.value().map(|v| v.0)
|
||||
}
|
||||
|
||||
fn spread_d(&self) -> Option<[bool; 28]> {
|
||||
fn spread_d(&self) -> Value<[bool; 28]> {
|
||||
self.spread_d.value().map(|v| v.0)
|
||||
}
|
||||
|
||||
fn xor_lower_sigma_0(&self) -> Option<[bool; 64]> {
|
||||
fn xor_lower_sigma_0(&self) -> Value<[bool; 64]> {
|
||||
self.spread_a()
|
||||
.zip(self.spread_b())
|
||||
.zip(self.spread_c())
|
||||
@@ -100,7 +104,7 @@ impl MessageScheduleConfig {
|
||||
fn decompose_subregion1_word(
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
word: Option<[bool; 32]>,
|
||||
word: Value<[bool; 32]>,
|
||||
index: usize,
|
||||
) -> Result<Subregion1Word, Error> {
|
||||
let row = get_word_row(index);
|
||||
@@ -117,7 +121,7 @@ impl MessageScheduleConfig {
|
||||
word[18..32].to_vec(),
|
||||
]
|
||||
});
|
||||
let pieces = transpose_option_vec(pieces, 4);
|
||||
let pieces = pieces.transpose_vec(4);
|
||||
|
||||
// Assign `a` (3-bit piece)
|
||||
let a =
|
||||
@@ -168,7 +172,7 @@ impl MessageScheduleConfig {
|
||||
|
||||
// Split `b` (4-bit chunk) into `b_hi` and `b_lo`
|
||||
// Assign `b_lo`, `spread_b_lo`
|
||||
let b_lo: Option<[bool; 2]> = word.b.value().map(|b| b.0[..2].try_into().unwrap());
|
||||
let b_lo: Value<[bool; 2]> = word.b.value().map(|b| b.0[..2].try_into().unwrap());
|
||||
let spread_b_lo = b_lo.map(spread_bits);
|
||||
{
|
||||
AssignedBits::<2>::assign_bits(region, || "b_lo", a_3, row - 1, b_lo)?;
|
||||
@@ -178,7 +182,7 @@ impl MessageScheduleConfig {
|
||||
|
||||
// Split `b` (2-bit chunk) into `b_hi` and `b_lo`
|
||||
// Assign `b_hi`, `spread_b_hi`
|
||||
let b_hi: Option<[bool; 2]> = word.b.value().map(|b| b.0[2..].try_into().unwrap());
|
||||
let b_hi: Value<[bool; 2]> = word.b.value().map(|b| b.0[2..].try_into().unwrap());
|
||||
let spread_b_hi = b_hi.map(spread_bits);
|
||||
{
|
||||
AssignedBits::<2>::assign_bits(region, || "b_hi", a_5, row - 1, b_hi)?;
|
||||
@@ -197,11 +201,11 @@ impl MessageScheduleConfig {
|
||||
|
||||
// Calculate R_0^{even}, R_0^{odd}, R_1^{even}, R_1^{odd}
|
||||
let r = word.xor_lower_sigma_0();
|
||||
let r_0: Option<[bool; 32]> = r.map(|r| r[..32].try_into().unwrap());
|
||||
let r_0: Value<[bool; 32]> = r.map(|r| r[..32].try_into().unwrap());
|
||||
let r_0_even = r_0.map(even_bits);
|
||||
let r_0_odd = r_0.map(odd_bits);
|
||||
|
||||
let r_1: Option<[bool; 32]> = r.map(|r| r[32..].try_into().unwrap());
|
||||
let r_1: Value<[bool; 32]> = r.map(|r| r[32..].try_into().unwrap());
|
||||
let r_1_even = r_1.map(even_bits);
|
||||
let r_1_odd = r_1.map(odd_bits);
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
use super::super::{util::*, AssignedBits, Bits, SpreadVar, SpreadWord, Table16Assignment};
|
||||
use super::{schedule_util::*, MessageScheduleConfig, MessageWord};
|
||||
use halo2_proofs::{circuit::Region, pasta::pallas, plonk::Error};
|
||||
use halo2_proofs::{
|
||||
circuit::{Region, Value},
|
||||
pasta::pallas,
|
||||
plonk::Error,
|
||||
};
|
||||
use std::convert::TryInto;
|
||||
|
||||
/// A word in subregion 2
|
||||
@@ -20,35 +24,35 @@ pub struct Subregion2Word {
|
||||
}
|
||||
|
||||
impl Subregion2Word {
|
||||
fn spread_a(&self) -> Option<[bool; 6]> {
|
||||
fn spread_a(&self) -> Value<[bool; 6]> {
|
||||
self.a.value().map(|v| v.spread())
|
||||
}
|
||||
|
||||
fn spread_b(&self) -> Option<[bool; 8]> {
|
||||
fn spread_b(&self) -> Value<[bool; 8]> {
|
||||
self.b.value().map(|v| v.spread())
|
||||
}
|
||||
|
||||
fn spread_c(&self) -> Option<[bool; 6]> {
|
||||
fn spread_c(&self) -> Value<[bool; 6]> {
|
||||
self.c.value().map(|v| v.spread())
|
||||
}
|
||||
|
||||
fn spread_d(&self) -> Option<[bool; 14]> {
|
||||
fn spread_d(&self) -> Value<[bool; 14]> {
|
||||
self.spread_d.value().map(|v| v.0)
|
||||
}
|
||||
|
||||
fn spread_e(&self) -> Option<[bool; 2]> {
|
||||
fn spread_e(&self) -> Value<[bool; 2]> {
|
||||
self.e.value().map(|v| v.spread())
|
||||
}
|
||||
|
||||
fn spread_f(&self) -> Option<[bool; 2]> {
|
||||
fn spread_f(&self) -> Value<[bool; 2]> {
|
||||
self.f.value().map(|v| v.spread())
|
||||
}
|
||||
|
||||
fn spread_g(&self) -> Option<[bool; 26]> {
|
||||
fn spread_g(&self) -> Value<[bool; 26]> {
|
||||
self.spread_g.value().map(|v| v.0)
|
||||
}
|
||||
|
||||
fn xor_sigma_0(&self) -> Option<[bool; 64]> {
|
||||
fn xor_sigma_0(&self) -> Value<[bool; 64]> {
|
||||
self.spread_a()
|
||||
.zip(self.spread_b())
|
||||
.zip(self.spread_c())
|
||||
@@ -98,7 +102,7 @@ impl Subregion2Word {
|
||||
})
|
||||
}
|
||||
|
||||
fn xor_sigma_1(&self) -> Option<[bool; 64]> {
|
||||
fn xor_sigma_1(&self) -> Value<[bool; 64]> {
|
||||
self.spread_a()
|
||||
.zip(self.spread_b())
|
||||
.zip(self.spread_c())
|
||||
@@ -251,20 +255,13 @@ impl MessageScheduleConfig {
|
||||
|| format!("W_{}", new_word_idx),
|
||||
a_5,
|
||||
get_word_row(new_word_idx - 16) + 1,
|
||||
|| {
|
||||
word.map(|word| pallas::Base::from(word as u64))
|
||||
.ok_or(Error::Synthesis)
|
||||
},
|
||||
|| word.map(|word| pallas::Base::from(word as u64)),
|
||||
)?;
|
||||
region.assign_advice(
|
||||
|| format!("carry_{}", new_word_idx),
|
||||
a_9,
|
||||
get_word_row(new_word_idx - 16) + 1,
|
||||
|| {
|
||||
carry
|
||||
.map(|carry| pallas::Base::from(carry as u64))
|
||||
.ok_or(Error::Synthesis)
|
||||
},
|
||||
|| carry.map(|carry| pallas::Base::from(carry as u64)),
|
||||
)?;
|
||||
let (word, halves) = self.assign_word_and_halves(region, word, new_word_idx)?;
|
||||
w.push(MessageWord(word));
|
||||
@@ -294,7 +291,7 @@ impl MessageScheduleConfig {
|
||||
fn decompose_word(
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
word: Option<&Bits<32>>,
|
||||
word: Value<&Bits<32>>,
|
||||
index: usize,
|
||||
) -> Result<Subregion2Word, Error> {
|
||||
let row = get_word_row(index);
|
||||
@@ -310,7 +307,7 @@ impl MessageScheduleConfig {
|
||||
word[19..32].to_vec(),
|
||||
]
|
||||
});
|
||||
let pieces = transpose_option_vec(pieces, 7);
|
||||
let pieces = pieces.transpose_vec(7);
|
||||
|
||||
// Rename these here for ease of matching the gates to the specification.
|
||||
let a_3 = self.extras[0];
|
||||
@@ -320,14 +317,14 @@ impl MessageScheduleConfig {
|
||||
let a = AssignedBits::<3>::assign_bits(region, || "a", a_3, row - 1, pieces[0].clone())?;
|
||||
|
||||
// Assign `b` (4-bit piece) lookup
|
||||
let spread_b: Option<SpreadWord<4, 8>> = pieces[1].clone().map(SpreadWord::try_new);
|
||||
let spread_b: Value<SpreadWord<4, 8>> = pieces[1].clone().map(SpreadWord::try_new);
|
||||
let spread_b = SpreadVar::with_lookup(region, &self.lookup, row + 1, spread_b)?;
|
||||
|
||||
// Assign `c` (3-bit piece)
|
||||
let c = AssignedBits::<3>::assign_bits(region, || "c", a_4, row - 1, pieces[2].clone())?;
|
||||
|
||||
// Assign `d` (7-bit piece) lookup
|
||||
let spread_d: Option<SpreadWord<7, 14>> = pieces[3].clone().map(SpreadWord::try_new);
|
||||
let spread_d: Value<SpreadWord<7, 14>> = pieces[3].clone().map(SpreadWord::try_new);
|
||||
let spread_d = SpreadVar::with_lookup(region, &self.lookup, row, spread_d)?;
|
||||
|
||||
// Assign `e` (1-bit piece)
|
||||
@@ -378,7 +375,7 @@ impl MessageScheduleConfig {
|
||||
// Split `b` (4-bit chunk) into `b_hi` and `b_lo`
|
||||
// Assign `b_lo`, `spread_b_lo`
|
||||
|
||||
let b_lo: Option<[bool; 2]> = word.b.value().map(|b| b.0[..2].try_into().unwrap());
|
||||
let b_lo: Value<[bool; 2]> = word.b.value().map(|b| b.0[..2].try_into().unwrap());
|
||||
let spread_b_lo = b_lo.map(spread_bits);
|
||||
{
|
||||
AssignedBits::<2>::assign_bits(region, || "b_lo", a_3, row - 1, b_lo)?;
|
||||
@@ -388,7 +385,7 @@ impl MessageScheduleConfig {
|
||||
|
||||
// Split `b` (2-bit chunk) into `b_hi` and `b_lo`
|
||||
// Assign `b_hi`, `spread_b_hi`
|
||||
let b_hi: Option<[bool; 2]> = word.b.value().map(|b| b.0[2..].try_into().unwrap());
|
||||
let b_hi: Value<[bool; 2]> = word.b.value().map(|b| b.0[2..].try_into().unwrap());
|
||||
let spread_b_hi = b_hi.map(spread_bits);
|
||||
{
|
||||
AssignedBits::<2>::assign_bits(region, || "b_hi", a_5, row - 1, b_hi)?;
|
||||
@@ -433,11 +430,11 @@ impl MessageScheduleConfig {
|
||||
|
||||
// Calculate R_0^{even}, R_0^{odd}, R_1^{even}, R_1^{odd}
|
||||
let r = word.xor_sigma_0();
|
||||
let r_0: Option<[bool; 32]> = r.map(|r| r[..32].try_into().unwrap());
|
||||
let r_0: Value<[bool; 32]> = r.map(|r| r[..32].try_into().unwrap());
|
||||
let r_0_even = r_0.map(even_bits);
|
||||
let r_0_odd = r_0.map(odd_bits);
|
||||
|
||||
let r_1: Option<[bool; 32]> = r.map(|r| r[32..].try_into().unwrap());
|
||||
let r_1: Value<[bool; 32]> = r.map(|r| r[32..].try_into().unwrap());
|
||||
let r_1_even = r_1.map(even_bits);
|
||||
let r_1_odd = r_1.map(odd_bits);
|
||||
|
||||
@@ -468,11 +465,11 @@ impl MessageScheduleConfig {
|
||||
// Calculate R_0^{even}, R_0^{odd}, R_1^{even}, R_1^{odd}
|
||||
// Calculate R_0^{even}, R_0^{odd}, R_1^{even}, R_1^{odd}
|
||||
let r = word.xor_sigma_1();
|
||||
let r_0: Option<[bool; 32]> = r.map(|r| r[..32].try_into().unwrap());
|
||||
let r_0: Value<[bool; 32]> = r.map(|r| r[..32].try_into().unwrap());
|
||||
let r_0_even = r_0.map(even_bits);
|
||||
let r_0_odd = r_0.map(odd_bits);
|
||||
|
||||
let r_1: Option<[bool; 32]> = r.map(|r| r[32..].try_into().unwrap());
|
||||
let r_1: Value<[bool; 32]> = r.map(|r| r[32..].try_into().unwrap());
|
||||
let r_1_even = r_1.map(even_bits);
|
||||
let r_1_odd = r_1.map(odd_bits);
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
use super::super::{util::*, AssignedBits, Bits, SpreadVar, SpreadWord, Table16Assignment};
|
||||
use super::{schedule_util::*, MessageScheduleConfig, MessageWord};
|
||||
use halo2_proofs::{circuit::Region, pasta::pallas, plonk::Error};
|
||||
use halo2_proofs::{
|
||||
circuit::{Region, Value},
|
||||
pasta::pallas,
|
||||
plonk::Error,
|
||||
};
|
||||
use std::convert::TryInto;
|
||||
|
||||
// A word in subregion 3
|
||||
@@ -18,23 +22,23 @@ pub struct Subregion3Word {
|
||||
}
|
||||
|
||||
impl Subregion3Word {
|
||||
fn spread_a(&self) -> Option<[bool; 20]> {
|
||||
fn spread_a(&self) -> Value<[bool; 20]> {
|
||||
self.spread_a.value().map(|v| v.0)
|
||||
}
|
||||
|
||||
fn spread_b(&self) -> Option<[bool; 14]> {
|
||||
fn spread_b(&self) -> Value<[bool; 14]> {
|
||||
self.b.value().map(|v| v.spread())
|
||||
}
|
||||
|
||||
fn spread_c(&self) -> Option<[bool; 4]> {
|
||||
fn spread_c(&self) -> Value<[bool; 4]> {
|
||||
self.c.value().map(|v| v.spread())
|
||||
}
|
||||
|
||||
fn spread_d(&self) -> Option<[bool; 26]> {
|
||||
fn spread_d(&self) -> Value<[bool; 26]> {
|
||||
self.spread_d.value().map(|v| v.0)
|
||||
}
|
||||
|
||||
fn xor_lower_sigma_1(&self) -> Option<[bool; 64]> {
|
||||
fn xor_lower_sigma_1(&self) -> Value<[bool; 64]> {
|
||||
self.spread_a()
|
||||
.zip(self.spread_b())
|
||||
.zip(self.spread_c())
|
||||
@@ -167,20 +171,13 @@ impl MessageScheduleConfig {
|
||||
|| format!("W_{}", new_word_idx),
|
||||
a_5,
|
||||
get_word_row(new_word_idx - 16) + 1,
|
||||
|| {
|
||||
word.map(|word| pallas::Base::from(word as u64))
|
||||
.ok_or(Error::Synthesis)
|
||||
},
|
||||
|| word.map(|word| pallas::Base::from(word as u64)),
|
||||
)?;
|
||||
region.assign_advice(
|
||||
|| format!("carry_{}", new_word_idx),
|
||||
a_9,
|
||||
get_word_row(new_word_idx - 16) + 1,
|
||||
|| {
|
||||
carry
|
||||
.map(|carry| pallas::Base::from(carry as u64))
|
||||
.ok_or(Error::Synthesis)
|
||||
},
|
||||
|| carry.map(|carry| pallas::Base::from(carry as u64)),
|
||||
)?;
|
||||
let (word, halves) = self.assign_word_and_halves(region, word, new_word_idx)?;
|
||||
w.push(MessageWord(word));
|
||||
@@ -200,7 +197,7 @@ impl MessageScheduleConfig {
|
||||
fn decompose_subregion3_word(
|
||||
&self,
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
word: Option<&Bits<32>>,
|
||||
word: Value<&Bits<32>>,
|
||||
index: usize,
|
||||
) -> Result<Subregion3Word, Error> {
|
||||
let row = get_word_row(index);
|
||||
@@ -217,7 +214,7 @@ impl MessageScheduleConfig {
|
||||
word[19..32].to_vec(),
|
||||
]
|
||||
});
|
||||
let pieces = transpose_option_vec(pieces, 4);
|
||||
let pieces = pieces.transpose_vec(4);
|
||||
|
||||
// Assign `a` (10-bit piece)
|
||||
let spread_a = pieces[0].clone().map(SpreadWord::try_new);
|
||||
@@ -264,21 +261,21 @@ impl MessageScheduleConfig {
|
||||
|
||||
// b_lo (2-bit chunk)
|
||||
{
|
||||
let b_lo: Option<[bool; 2]> = word.b.value().map(|v| v[0..2].try_into().unwrap());
|
||||
let b_lo: Value<[bool; 2]> = word.b.value().map(|v| v[0..2].try_into().unwrap());
|
||||
let b_lo = b_lo.map(SpreadWord::<2, 4>::new);
|
||||
SpreadVar::without_lookup(region, a_3, row - 1, a_4, row - 1, b_lo)?;
|
||||
}
|
||||
|
||||
// b_mid (2-bit chunk)
|
||||
{
|
||||
let b_mid: Option<[bool; 2]> = word.b.value().map(|v| v[2..4].try_into().unwrap());
|
||||
let b_mid: Value<[bool; 2]> = word.b.value().map(|v| v[2..4].try_into().unwrap());
|
||||
let b_mid = b_mid.map(SpreadWord::<2, 4>::new);
|
||||
SpreadVar::without_lookup(region, a_5, row - 1, a_6, row - 1, b_mid)?;
|
||||
}
|
||||
|
||||
// b_hi (3-bit chunk)
|
||||
{
|
||||
let b_hi: Option<[bool; 3]> = word.b.value().map(|v| v[4..7].try_into().unwrap());
|
||||
let b_hi: Value<[bool; 3]> = word.b.value().map(|v| v[4..7].try_into().unwrap());
|
||||
let b_hi = b_hi.map(SpreadWord::<3, 6>::new);
|
||||
SpreadVar::without_lookup(region, a_5, row + 1, a_6, row + 1, b_hi)?;
|
||||
}
|
||||
@@ -301,11 +298,11 @@ impl MessageScheduleConfig {
|
||||
// (10, 7, 2, 13)
|
||||
// Calculate R_0^{even}, R_0^{odd}, R_1^{even}, R_1^{odd}
|
||||
let r = word.xor_lower_sigma_1();
|
||||
let r_0: Option<[bool; 32]> = r.map(|r| r[..32].try_into().unwrap());
|
||||
let r_0: Value<[bool; 32]> = r.map(|r| r[..32].try_into().unwrap());
|
||||
let r_0_even = r_0.map(even_bits);
|
||||
let r_0_odd = r_0.map(odd_bits);
|
||||
|
||||
let r_1: Option<[bool; 32]> = r.map(|r| r[32..].try_into().unwrap());
|
||||
let r_1: Value<[bool; 32]> = r.map(|r| r[32..].try_into().unwrap());
|
||||
let r_1_even = r_1.map(even_bits);
|
||||
let r_1_odd = r_1.map(odd_bits);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::{util::*, AssignedBits};
|
||||
use halo2_proofs::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::{Chip, Layouter, Region},
|
||||
circuit::{Chip, Layouter, Region, Value},
|
||||
pasta::pallas,
|
||||
plonk::{Advice, Column, ConstraintSystem, Error, TableColumn},
|
||||
poly::Rotation,
|
||||
@@ -68,7 +68,7 @@ impl<const DENSE: usize, const SPREAD: usize> SpreadWord<DENSE, SPREAD> {
|
||||
/// A variable stored in advice columns corresponding to a row of [`SpreadTableConfig`].
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) struct SpreadVar<const DENSE: usize, const SPREAD: usize> {
|
||||
pub tag: Option<u8>,
|
||||
pub tag: Value<u8>,
|
||||
pub dense: AssignedBits<DENSE>,
|
||||
pub spread: AssignedBits<SPREAD>,
|
||||
}
|
||||
@@ -78,7 +78,7 @@ impl<const DENSE: usize, const SPREAD: usize> SpreadVar<DENSE, SPREAD> {
|
||||
region: &mut Region<'_, pallas::Base>,
|
||||
cols: &SpreadInputs,
|
||||
row: usize,
|
||||
word: Option<SpreadWord<DENSE, SPREAD>>,
|
||||
word: Value<SpreadWord<DENSE, SPREAD>>,
|
||||
) -> Result<Self, Error> {
|
||||
let tag = word.map(|word| word.tag);
|
||||
let dense_val = word.map(|word| word.dense);
|
||||
@@ -88,10 +88,7 @@ impl<const DENSE: usize, const SPREAD: usize> SpreadVar<DENSE, SPREAD> {
|
||||
|| "tag",
|
||||
cols.tag,
|
||||
row,
|
||||
|| {
|
||||
tag.map(|tag| pallas::Base::from(tag as u64))
|
||||
.ok_or(Error::Synthesis)
|
||||
},
|
||||
|| tag.map(|tag| pallas::Base::from(tag as u64)),
|
||||
)?;
|
||||
|
||||
let dense =
|
||||
@@ -109,7 +106,7 @@ impl<const DENSE: usize, const SPREAD: usize> SpreadVar<DENSE, SPREAD> {
|
||||
dense_row: usize,
|
||||
spread_col: Column<Advice>,
|
||||
spread_row: usize,
|
||||
word: Option<SpreadWord<DENSE, SPREAD>>,
|
||||
word: Value<SpreadWord<DENSE, SPREAD>>,
|
||||
) -> Result<Self, Error> {
|
||||
let tag = word.map(|word| word.tag);
|
||||
let dense_val = word.map(|word| word.dense);
|
||||
@@ -229,20 +226,20 @@ impl<F: FieldExt> SpreadTableChip<F> {
|
||||
index,
|
||||
|| {
|
||||
row = rows.next();
|
||||
row.map(|(tag, _, _)| tag).ok_or(Error::Synthesis)
|
||||
Value::known(row.map(|(tag, _, _)| tag).unwrap())
|
||||
},
|
||||
)?;
|
||||
table.assign_cell(
|
||||
|| "dense",
|
||||
config.table.dense,
|
||||
index,
|
||||
|| row.map(|(_, dense, _)| dense).ok_or(Error::Synthesis),
|
||||
|| Value::known(row.map(|(_, dense, _)| dense).unwrap()),
|
||||
)?;
|
||||
table.assign_cell(
|
||||
|| "spread",
|
||||
config.table.spread,
|
||||
index,
|
||||
|| row.map(|(_, _, spread)| spread).ok_or(Error::Synthesis),
|
||||
|| Value::known(row.map(|(_, _, spread)| spread).unwrap()),
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -292,7 +289,7 @@ mod tests {
|
||||
|
||||
use halo2_proofs::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::{Layouter, SimpleFloorPlanner},
|
||||
circuit::{Layouter, SimpleFloorPlanner, Value},
|
||||
dev::MockProver,
|
||||
pasta::Fp,
|
||||
plonk::{Advice, Circuit, Column, ConstraintSystem, Error},
|
||||
@@ -334,13 +331,23 @@ mod tests {
|
||||
|mut gate| {
|
||||
let mut row = 0;
|
||||
let mut add_row = |tag, dense, spread| -> Result<(), Error> {
|
||||
gate.assign_advice(|| "tag", config.input.tag, row, || Ok(tag))?;
|
||||
gate.assign_advice(|| "dense", config.input.dense, row, || Ok(dense))?;
|
||||
gate.assign_advice(
|
||||
|| "tag",
|
||||
config.input.tag,
|
||||
row,
|
||||
|| Value::known(tag),
|
||||
)?;
|
||||
gate.assign_advice(
|
||||
|| "dense",
|
||||
config.input.dense,
|
||||
row,
|
||||
|| Value::known(dense),
|
||||
)?;
|
||||
gate.assign_advice(
|
||||
|| "spread",
|
||||
config.input.spread,
|
||||
row,
|
||||
|| Ok(spread),
|
||||
|| Value::known(spread),
|
||||
)?;
|
||||
row += 1;
|
||||
Ok(())
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use halo2_proofs::circuit::Value;
|
||||
|
||||
pub const MASK_EVEN_32: u32 = 0x55555555;
|
||||
|
||||
/// The sequence of bits representing a u64 in little-endian order.
|
||||
@@ -96,25 +98,15 @@ pub fn odd_bits<const LEN: usize, const HALF: usize>(bits: [bool; LEN]) -> [bool
|
||||
odd_bits
|
||||
}
|
||||
|
||||
/// Helper function to transpose an Option<Vec<F>> to a Vec<Option<F>>.
|
||||
/// The length of the vector must be `len`.
|
||||
pub fn transpose_option_vec<T: Clone>(vec: Option<Vec<T>>, len: usize) -> Vec<Option<T>> {
|
||||
if let Some(vec) = vec {
|
||||
vec.into_iter().map(Some).collect()
|
||||
} else {
|
||||
vec![None; len]
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a vector of words as vec![(lo: u16, hi: u16)], returns their sum: u32, along
|
||||
/// with a carry bit.
|
||||
pub fn sum_with_carry(words: Vec<(Option<u16>, Option<u16>)>) -> (Option<u32>, Option<u64>) {
|
||||
let words_lo: Option<Vec<u64>> = words.iter().map(|(lo, _)| lo.map(|lo| lo as u64)).collect();
|
||||
let words_hi: Option<Vec<u64>> = words.iter().map(|(_, hi)| hi.map(|hi| hi as u64)).collect();
|
||||
pub fn sum_with_carry(words: Vec<(Value<u16>, Value<u16>)>) -> (Value<u32>, Value<u64>) {
|
||||
let words_lo: Value<Vec<u64>> = words.iter().map(|(lo, _)| lo.map(|lo| lo as u64)).collect();
|
||||
let words_hi: Value<Vec<u64>> = words.iter().map(|(_, hi)| hi.map(|hi| hi as u64)).collect();
|
||||
|
||||
let sum: Option<u64> = {
|
||||
let sum_lo: Option<u64> = words_lo.map(|vec| vec.iter().sum());
|
||||
let sum_hi: Option<u64> = words_hi.map(|vec| vec.iter().sum());
|
||||
let sum: Value<u64> = {
|
||||
let sum_lo: Value<u64> = words_lo.map(|vec| vec.iter().sum());
|
||||
let sum_hi: Value<u64> = words_hi.map(|vec| vec.iter().sum());
|
||||
sum_lo.zip(sum_hi).map(|(lo, hi)| lo + (1 << 16) * hi)
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,10 @@ use crate::{
|
||||
utilities::{FieldValue, RangeConstrained, Var},
|
||||
};
|
||||
use group::ff::{Field, PrimeField};
|
||||
use halo2_proofs::{circuit::Layouter, plonk::Error};
|
||||
use halo2_proofs::{
|
||||
circuit::{Layouter, Value},
|
||||
plonk::Error,
|
||||
};
|
||||
use pasta_curves::arithmetic::CurveAffine;
|
||||
use std::fmt::Debug;
|
||||
|
||||
@@ -67,7 +70,7 @@ pub trait SinsemillaInstructions<C: CurveAffine, const K: usize, const MAX_WORDS
|
||||
fn witness_message_piece(
|
||||
&self,
|
||||
layouter: impl Layouter<C::Base>,
|
||||
value: Option<C::Base>,
|
||||
value: Value<C::Base>,
|
||||
num_words: usize,
|
||||
) -> Result<Self::MessagePiece, Error>;
|
||||
|
||||
@@ -111,7 +114,7 @@ where
|
||||
fn from_bitstring(
|
||||
chip: SinsemillaChip,
|
||||
mut layouter: impl Layouter<C::Base>,
|
||||
bitstring: Vec<Option<bool>>,
|
||||
bitstring: Vec<Value<bool>>,
|
||||
) -> Result<Self, Error> {
|
||||
// Message must be composed of `K`-bit words.
|
||||
assert_eq!(bitstring.len() % K, 0);
|
||||
@@ -185,7 +188,7 @@ where
|
||||
fn from_bitstring(
|
||||
chip: SinsemillaChip,
|
||||
layouter: impl Layouter<C::Base>,
|
||||
bitstring: &[Option<bool>],
|
||||
bitstring: &[Value<bool>],
|
||||
) -> Result<Self, Error> {
|
||||
// Message must be composed of `K`-bit words.
|
||||
assert_eq!(bitstring.len() % K, 0);
|
||||
@@ -197,8 +200,8 @@ where
|
||||
assert!(num_words <= piece_max_num_words as usize);
|
||||
|
||||
// Closure to parse a bitstring (little-endian) into a base field element.
|
||||
let to_base_field = |bits: &[Option<bool>]| -> Option<C::Base> {
|
||||
let bits: Option<Vec<bool>> = bits.iter().cloned().collect();
|
||||
let to_base_field = |bits: &[Value<bool>]| -> Value<C::Base> {
|
||||
let bits: Value<Vec<bool>> = bits.iter().cloned().collect();
|
||||
bits.map(|bits| {
|
||||
bits.into_iter().rev().fold(C::Base::zero(), |acc, bit| {
|
||||
if bit {
|
||||
@@ -218,7 +221,7 @@ where
|
||||
pub fn from_field_elem(
|
||||
chip: SinsemillaChip,
|
||||
layouter: impl Layouter<C::Base>,
|
||||
field_elem: Option<C::Base>,
|
||||
field_elem: Value<C::Base>,
|
||||
num_words: usize,
|
||||
) -> Result<Self, Error> {
|
||||
let inner = chip.witness_message_piece(layouter, field_elem, num_words)?;
|
||||
@@ -237,22 +240,19 @@ where
|
||||
pub fn from_subpieces(
|
||||
chip: SinsemillaChip,
|
||||
layouter: impl Layouter<C::Base>,
|
||||
subpieces: impl IntoIterator<Item = RangeConstrained<C::Base, Option<C::Base>>>,
|
||||
subpieces: impl IntoIterator<Item = RangeConstrained<C::Base, Value<C::Base>>>,
|
||||
) -> Result<Self, Error> {
|
||||
let (field_elem, total_bits) =
|
||||
subpieces
|
||||
.into_iter()
|
||||
.fold((Some(C::Base::zero()), 0), |(acc, bits), subpiece| {
|
||||
assert!(bits < 64);
|
||||
let subpiece_shifted = subpiece
|
||||
.inner()
|
||||
.value()
|
||||
.map(|v| C::Base::from(1 << bits) * v);
|
||||
(
|
||||
acc.zip(subpiece_shifted).map(|(a, b)| a + b),
|
||||
bits + subpiece.num_bits(),
|
||||
)
|
||||
});
|
||||
let (field_elem, total_bits) = subpieces.into_iter().fold(
|
||||
(Value::known(C::Base::zero()), 0),
|
||||
|(acc, bits), subpiece| {
|
||||
assert!(bits < 64);
|
||||
let subpiece_shifted = subpiece
|
||||
.inner()
|
||||
.value()
|
||||
.map(|v| C::Base::from(1 << bits) * v);
|
||||
(acc + subpiece_shifted, bits + subpiece.num_bits())
|
||||
},
|
||||
);
|
||||
|
||||
// Message must be composed of `K`-bit words.
|
||||
assert_eq!(total_bits % K, 0);
|
||||
@@ -453,7 +453,7 @@ where
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use halo2_proofs::{
|
||||
circuit::{Layouter, SimpleFloorPlanner},
|
||||
circuit::{Layouter, SimpleFloorPlanner, Value},
|
||||
dev::MockProver,
|
||||
plonk::{Circuit, ConstraintSystem, Error},
|
||||
};
|
||||
@@ -615,7 +615,7 @@ pub(crate) mod tests {
|
||||
let merkle_crh = HashDomain::new(chip1.clone(), ecc_chip.clone(), &TestHashDomain);
|
||||
|
||||
// Layer 31, l = MERKLE_DEPTH - 1 - layer = 0
|
||||
let l_bitstring = vec![Some(false); K];
|
||||
let l_bitstring = vec![Value::known(false); K];
|
||||
let l = MessagePiece::from_bitstring(
|
||||
chip1.clone(),
|
||||
layouter.namespace(|| "l"),
|
||||
@@ -623,8 +623,9 @@ pub(crate) mod tests {
|
||||
)?;
|
||||
|
||||
// Left leaf
|
||||
let left_bitstring: Vec<Option<bool>> =
|
||||
(0..250).map(|_| Some(rand::random::<bool>())).collect();
|
||||
let left_bitstring: Vec<Value<bool>> = (0..250)
|
||||
.map(|_| Value::known(rand::random::<bool>()))
|
||||
.collect();
|
||||
let left = MessagePiece::from_bitstring(
|
||||
chip1.clone(),
|
||||
layouter.namespace(|| "left"),
|
||||
@@ -632,35 +633,34 @@ pub(crate) mod tests {
|
||||
)?;
|
||||
|
||||
// Right leaf
|
||||
let right_bitstring: Vec<Option<bool>> =
|
||||
(0..250).map(|_| Some(rand::random::<bool>())).collect();
|
||||
let right_bitstring: Vec<Value<bool>> = (0..250)
|
||||
.map(|_| Value::known(rand::random::<bool>()))
|
||||
.collect();
|
||||
let right = MessagePiece::from_bitstring(
|
||||
chip1.clone(),
|
||||
layouter.namespace(|| "right"),
|
||||
&right_bitstring,
|
||||
)?;
|
||||
|
||||
let l_bitstring: Option<Vec<bool>> = l_bitstring.into_iter().collect();
|
||||
let left_bitstring: Option<Vec<bool>> = left_bitstring.into_iter().collect();
|
||||
let right_bitstring: Option<Vec<bool>> = right_bitstring.into_iter().collect();
|
||||
let l_bitstring: Value<Vec<bool>> = l_bitstring.into_iter().collect();
|
||||
let left_bitstring: Value<Vec<bool>> = left_bitstring.into_iter().collect();
|
||||
let right_bitstring: Value<Vec<bool>> = right_bitstring.into_iter().collect();
|
||||
|
||||
// Witness expected parent
|
||||
let expected_parent = {
|
||||
let expected_parent = if let (Some(l), Some(left), Some(right)) =
|
||||
(l_bitstring, left_bitstring, right_bitstring)
|
||||
{
|
||||
let merkle_crh = sinsemilla::HashDomain::from_Q((*Q).into());
|
||||
let point = merkle_crh
|
||||
.hash_to_point(
|
||||
l.into_iter()
|
||||
.chain(left.into_iter())
|
||||
.chain(right.into_iter()),
|
||||
)
|
||||
.unwrap();
|
||||
Some(point.to_affine())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let expected_parent = l_bitstring.zip(left_bitstring.zip(right_bitstring)).map(
|
||||
|(l, (left, right))| {
|
||||
let merkle_crh = sinsemilla::HashDomain::from_Q((*Q).into());
|
||||
let point = merkle_crh
|
||||
.hash_to_point(
|
||||
l.into_iter()
|
||||
.chain(left.into_iter())
|
||||
.chain(right.into_iter()),
|
||||
)
|
||||
.unwrap();
|
||||
point.to_affine()
|
||||
},
|
||||
);
|
||||
|
||||
NonIdentityPoint::new(
|
||||
ecc_chip.clone(),
|
||||
@@ -687,14 +687,15 @@ pub(crate) mod tests {
|
||||
let test_commit =
|
||||
CommitDomain::new(chip2.clone(), ecc_chip.clone(), &TestCommitDomain);
|
||||
let r_val = pallas::Scalar::random(rng);
|
||||
let message: Vec<Option<bool>> =
|
||||
(0..500).map(|_| Some(rand::random::<bool>())).collect();
|
||||
let message: Vec<Value<bool>> = (0..500)
|
||||
.map(|_| Value::known(rand::random::<bool>()))
|
||||
.collect();
|
||||
|
||||
let (result, _) = {
|
||||
let r = ScalarFixed::new(
|
||||
ecc_chip.clone(),
|
||||
layouter.namespace(|| "r"),
|
||||
Some(r_val),
|
||||
Value::known(r_val),
|
||||
)?;
|
||||
let message = Message::from_bitstring(
|
||||
chip2,
|
||||
@@ -706,14 +707,12 @@ pub(crate) mod tests {
|
||||
|
||||
// Witness expected result.
|
||||
let expected_result = {
|
||||
let message: Option<Vec<bool>> = message.into_iter().collect();
|
||||
let expected_result = if let Some(message) = message {
|
||||
let message: Value<Vec<bool>> = message.into_iter().collect();
|
||||
let expected_result = message.map(|message| {
|
||||
let domain = sinsemilla::CommitDomain::new(PERSONALIZATION);
|
||||
let point = domain.commit(message.into_iter(), &r_val).unwrap();
|
||||
Some(point.to_affine())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
point.to_affine()
|
||||
});
|
||||
|
||||
NonIdentityPoint::new(
|
||||
ecc_chip,
|
||||
|
||||
@@ -14,7 +14,7 @@ use crate::{
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use halo2_proofs::{
|
||||
circuit::{AssignedCell, Chip, Layouter},
|
||||
circuit::{AssignedCell, Chip, Layouter, Value},
|
||||
plonk::{
|
||||
Advice, Column, ConstraintSystem, Constraints, Error, Expression, Fixed, Selector,
|
||||
TableColumn, VirtualCells,
|
||||
@@ -288,7 +288,7 @@ where
|
||||
fn witness_message_piece(
|
||||
&self,
|
||||
mut layouter: impl Layouter<pallas::Base>,
|
||||
field_elem: Option<pallas::Base>,
|
||||
field_elem: Value<pallas::Base>,
|
||||
num_words: usize,
|
||||
) -> Result<Self::MessagePiece, Error> {
|
||||
let config = self.config().clone();
|
||||
@@ -300,7 +300,7 @@ where
|
||||
|| "witness message piece",
|
||||
config.witness_pieces,
|
||||
0,
|
||||
|| field_elem.ok_or(Error::Synthesis),
|
||||
|| field_elem,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use halo2_proofs::{
|
||||
circuit::Layouter,
|
||||
circuit::{Layouter, Value},
|
||||
plonk::{ConstraintSystem, Error, Expression, TableColumn},
|
||||
poly::Rotation,
|
||||
};
|
||||
@@ -85,10 +85,10 @@ impl GeneratorTableConfig {
|
||||
|| "table_idx",
|
||||
self.table_idx,
|
||||
index,
|
||||
|| Ok(pallas::Base::from(index as u64)),
|
||||
|| Value::known(pallas::Base::from(index as u64)),
|
||||
)?;
|
||||
table.assign_cell(|| "table_x", self.table_x, index, || Ok(*x))?;
|
||||
table.assign_cell(|| "table_y", self.table_y, index, || Ok(*y))?;
|
||||
table.assign_cell(|| "table_x", self.table_x, index, || Value::known(*x))?;
|
||||
table.assign_cell(|| "table_y", self.table_y, index, || Value::known(*y))?;
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
|
||||
@@ -5,9 +5,8 @@ use crate::{
|
||||
sinsemilla::primitives::{self as sinsemilla, lebs2ip_k, INV_TWO_POW_K, SINSEMILLA_S},
|
||||
};
|
||||
|
||||
use halo2_proofs::circuit::AssignedCell;
|
||||
use halo2_proofs::{
|
||||
circuit::{Chip, Region},
|
||||
circuit::{AssignedCell, Chip, Region, Value},
|
||||
plonk::{Assigned, Error},
|
||||
};
|
||||
|
||||
@@ -56,9 +55,14 @@ where
|
||||
let mut y_a: Y<pallas::Base> = {
|
||||
// Enable `q_sinsemilla4` on the first row.
|
||||
config.q_sinsemilla4.enable(region, offset)?;
|
||||
region.assign_fixed(|| "fixed y_q", config.fixed_y_q, offset, || Ok(y_q))?;
|
||||
region.assign_fixed(
|
||||
|| "fixed y_q",
|
||||
config.fixed_y_q,
|
||||
offset,
|
||||
|| Value::known(y_q),
|
||||
)?;
|
||||
|
||||
(Some(y_q.into())).into()
|
||||
Value::known(y_q.into()).into()
|
||||
};
|
||||
|
||||
// Constrain the initial x_q to equal the x-coordinate of the domain's `Q`.
|
||||
@@ -95,12 +99,8 @@ where
|
||||
// Assign the final y_a.
|
||||
let y_a = {
|
||||
// Assign the final y_a.
|
||||
let y_a_cell = region.assign_advice(
|
||||
|| "y_a",
|
||||
config.double_and_add.lambda_1,
|
||||
offset,
|
||||
|| y_a.ok_or(Error::Synthesis),
|
||||
)?;
|
||||
let y_a_cell =
|
||||
region.assign_advice(|| "y_a", config.double_and_add.lambda_1, offset, || y_a.0)?;
|
||||
|
||||
// Assign lambda_2 and x_p zero values since they are queried
|
||||
// in the gate. (The actual values do not matter since they are
|
||||
@@ -110,13 +110,13 @@ where
|
||||
|| "dummy lambda2",
|
||||
config.double_and_add.lambda_2,
|
||||
offset,
|
||||
|| Ok(pallas::Base::zero()),
|
||||
|| Value::known(pallas::Base::zero()),
|
||||
)?;
|
||||
region.assign_advice(
|
||||
|| "dummy x_p",
|
||||
config.double_and_add.x_p,
|
||||
offset,
|
||||
|| Ok(pallas::Base::zero()),
|
||||
|| Value::known(pallas::Base::zero()),
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -127,55 +127,44 @@ where
|
||||
#[allow(non_snake_case)]
|
||||
// Check equivalence to result from primitives::sinsemilla::hash_to_point
|
||||
{
|
||||
use crate::sinsemilla::{
|
||||
message::MessagePiece,
|
||||
primitives::{K, S_PERSONALIZATION},
|
||||
};
|
||||
use crate::sinsemilla::primitives::{K, S_PERSONALIZATION};
|
||||
|
||||
use group::{prime::PrimeCurveAffine, Curve};
|
||||
use pasta_curves::arithmetic::CurveExt;
|
||||
|
||||
let field_elems: Option<Vec<pallas::Base>> =
|
||||
message.iter().map(|piece| piece.field_elem()).collect();
|
||||
let field_elems: Value<Vec<_>> = message
|
||||
.iter()
|
||||
.map(|piece| piece.field_elem().map(|elem| (elem, piece.num_words())))
|
||||
.collect();
|
||||
|
||||
if field_elems.is_some() && x_a.value().is_some() && y_a.value().is_some() {
|
||||
// Get message as a bitstring.
|
||||
let bitstring: Vec<bool> = message
|
||||
.iter()
|
||||
.flat_map(|piece: &MessagePiece<pallas::Base, K>| {
|
||||
piece
|
||||
.field_elem()
|
||||
.unwrap()
|
||||
.to_le_bits()
|
||||
.into_iter()
|
||||
.take(K * piece.num_words())
|
||||
})
|
||||
.collect();
|
||||
field_elems
|
||||
.zip(x_a.value().zip(y_a.value()))
|
||||
.assert_if_known(|(field_elems, (x_a, y_a))| {
|
||||
// Get message as a bitstring.
|
||||
let bitstring: Vec<bool> = field_elems
|
||||
.iter()
|
||||
.flat_map(|(elem, num_words)| {
|
||||
elem.to_le_bits().into_iter().take(K * num_words)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let hasher_S = pallas::Point::hash_to_curve(S_PERSONALIZATION);
|
||||
let S = |chunk: &[bool]| hasher_S(&lebs2ip_k(chunk).to_le_bytes());
|
||||
let hasher_S = pallas::Point::hash_to_curve(S_PERSONALIZATION);
|
||||
let S = |chunk: &[bool]| hasher_S(&lebs2ip_k(chunk).to_le_bytes());
|
||||
|
||||
// We can use complete addition here because it differs from
|
||||
// incomplete addition with negligible probability.
|
||||
let expected_point = bitstring
|
||||
.chunks(K)
|
||||
.fold(Q.to_curve(), |acc, chunk| (acc + S(chunk)) + acc);
|
||||
let actual_point = pallas::Affine::from_xy(
|
||||
x_a.value().unwrap().evaluate(),
|
||||
y_a.value().unwrap().evaluate(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(expected_point.to_affine(), actual_point);
|
||||
}
|
||||
// We can use complete addition here because it differs from
|
||||
// incomplete addition with negligible probability.
|
||||
let expected_point = bitstring
|
||||
.chunks(K)
|
||||
.fold(Q.to_curve(), |acc, chunk| (acc + S(chunk)) + acc);
|
||||
let actual_point =
|
||||
pallas::Affine::from_xy(x_a.evaluate(), y_a.evaluate()).unwrap();
|
||||
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.is_zero_vartime() || y_a.is_zero_vartime() {
|
||||
return Err(Error::Synthesis);
|
||||
}
|
||||
}
|
||||
}
|
||||
x_a.value()
|
||||
.zip(y_a.value())
|
||||
.error_if_known_and(|(x_a, y_a)| x_a.is_zero_vartime() || y_a.is_zero_vartime())?;
|
||||
Ok((
|
||||
NonIdentityEccPoint::from_coordinates_unchecked(x_a.0, y_a),
|
||||
zs_sum,
|
||||
@@ -224,7 +213,7 @@ where
|
||||
|| "q_s2 = 1",
|
||||
config.q_sinsemilla2,
|
||||
offset + row,
|
||||
|| Ok(pallas::Base::one()),
|
||||
|| Value::known(pallas::Base::one()),
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -241,7 +230,7 @@ where
|
||||
config.q_sinsemilla2,
|
||||
offset + piece.num_words() - 1,
|
||||
|| {
|
||||
Ok(if final_piece {
|
||||
Value::known(if final_piece {
|
||||
pallas::Base::from(2)
|
||||
} else {
|
||||
pallas::Base::zero()
|
||||
@@ -251,7 +240,7 @@ where
|
||||
}
|
||||
|
||||
// Message piece as K * piece.length bitstring
|
||||
let bitstring: Option<Vec<bool>> = piece.field_elem().map(|value| {
|
||||
let bitstring: Value<Vec<bool>> = piece.field_elem().map(|value| {
|
||||
value
|
||||
.to_le_bits()
|
||||
.into_iter()
|
||||
@@ -259,7 +248,7 @@ where
|
||||
.collect()
|
||||
});
|
||||
|
||||
let words: Option<Vec<u32>> = bitstring.map(|bitstring| {
|
||||
let words: Value<Vec<u32>> = bitstring.map(|bitstring| {
|
||||
bitstring
|
||||
.chunks_exact(sinsemilla::K)
|
||||
.map(lebs2ip_k)
|
||||
@@ -267,19 +256,15 @@ where
|
||||
});
|
||||
|
||||
// Get (x_p, y_p) for each word.
|
||||
let generators: Option<Vec<(pallas::Base, pallas::Base)>> = words.clone().map(|words| {
|
||||
let generators: Value<Vec<(pallas::Base, pallas::Base)>> = words.clone().map(|words| {
|
||||
words
|
||||
.iter()
|
||||
.map(|word| SINSEMILLA_S[*word as usize])
|
||||
.collect()
|
||||
});
|
||||
|
||||
// Convert `words` from `Option<Vec<u32>>` to `Vec<Option<u32>>`
|
||||
let words: Vec<Option<u32>> = if let Some(words) = words {
|
||||
words.into_iter().map(Some).collect()
|
||||
} else {
|
||||
vec![None; piece.num_words()]
|
||||
};
|
||||
// Convert `words` from `Value<Vec<u32>>` to `Vec<Value<u32>>`
|
||||
let words = words.transpose_vec(piece.num_words());
|
||||
|
||||
// Decompose message piece into `K`-bit pieces with a running sum `z`.
|
||||
let zs = {
|
||||
@@ -302,19 +287,18 @@ where
|
||||
// We end up with z_n = 0. (z_n is not directly encoded as a cell value;
|
||||
// it is implicitly taken as 0 by adjusting the definition of m_{i+1}.)
|
||||
let mut z = piece.field_elem();
|
||||
let inv_2_k = pallas::Base::from_repr(INV_TWO_POW_K).unwrap();
|
||||
let inv_2_k = Value::known(pallas::Base::from_repr(INV_TWO_POW_K).unwrap());
|
||||
|
||||
// We do not assign the final z_n as it is constrained to be zero.
|
||||
for (idx, word) in words[0..(words.len() - 1)].iter().enumerate() {
|
||||
let word = word.map(|word| pallas::Base::from(word as u64));
|
||||
// z_{i + 1} = (z_i - m_{i + 1}) / 2^K
|
||||
z = z
|
||||
.zip(*word)
|
||||
.map(|(z, word)| (z - pallas::Base::from(word as u64)) * inv_2_k);
|
||||
z = (z - word) * inv_2_k;
|
||||
let cell = region.assign_advice(
|
||||
|| format!("z_{:?}", idx + 1),
|
||||
config.bits,
|
||||
offset + idx + 1,
|
||||
|| z.ok_or(Error::Synthesis),
|
||||
|| z,
|
||||
)?;
|
||||
zs.push(cell)
|
||||
}
|
||||
@@ -325,64 +309,43 @@ where
|
||||
// The accumulator x-coordinate provided by the caller MUST have been assigned
|
||||
// within this region.
|
||||
|
||||
let generators: Vec<Option<(pallas::Base, pallas::Base)>> =
|
||||
if let Some(generators) = generators {
|
||||
generators.into_iter().map(Some).collect()
|
||||
} else {
|
||||
vec![None; piece.num_words()]
|
||||
};
|
||||
let generators = generators.transpose_vec(piece.num_words());
|
||||
|
||||
for (row, gen) in generators.iter().enumerate() {
|
||||
let x_p = gen.map(|gen| gen.0);
|
||||
let y_p = gen.map(|gen| gen.1);
|
||||
|
||||
// Assign `x_p`
|
||||
region.assign_advice(
|
||||
|| "x_p",
|
||||
config.double_and_add.x_p,
|
||||
offset + row,
|
||||
|| x_p.ok_or(Error::Synthesis),
|
||||
)?;
|
||||
region.assign_advice(|| "x_p", config.double_and_add.x_p, offset + row, || x_p)?;
|
||||
|
||||
// Compute and assign `lambda_1`
|
||||
let lambda_1 = {
|
||||
let lambda_1 = x_a
|
||||
.value()
|
||||
.zip(y_a.0)
|
||||
.zip(x_p)
|
||||
.zip(y_p)
|
||||
.map(|(((x_a, y_a), x_p), y_p)| (y_a - y_p) * (x_a - x_p).invert());
|
||||
let lambda_1 = (y_a.0 - y_p) * (x_a.value() - x_p).invert();
|
||||
|
||||
// Assign lambda_1
|
||||
region.assign_advice(
|
||||
|| "lambda_1",
|
||||
config.double_and_add.lambda_1,
|
||||
offset + row,
|
||||
|| lambda_1.ok_or(Error::Synthesis),
|
||||
|| lambda_1,
|
||||
)?;
|
||||
|
||||
lambda_1
|
||||
};
|
||||
|
||||
// Compute `x_r`
|
||||
let x_r = lambda_1
|
||||
.zip(x_a.value())
|
||||
.zip(x_p)
|
||||
.map(|((lambda_1, x_a), x_p)| lambda_1.square() - x_a - x_p);
|
||||
let x_r = lambda_1.square() - x_a.value() - x_p;
|
||||
|
||||
// Compute and assign `lambda_2`
|
||||
let lambda_2 = {
|
||||
let lambda_2 = x_a.value().zip(y_a.0).zip(x_r).zip(lambda_1).map(
|
||||
|(((x_a, y_a), x_r), lambda_1)| {
|
||||
y_a * pallas::Base::from(2) * (x_a - x_r).invert() - lambda_1
|
||||
},
|
||||
);
|
||||
let lambda_2 =
|
||||
y_a.0 * pallas::Base::from(2) * (x_a.value() - x_r).invert() - lambda_1;
|
||||
|
||||
region.assign_advice(
|
||||
|| "lambda_2",
|
||||
config.double_and_add.lambda_2,
|
||||
offset + row,
|
||||
|| lambda_2.ok_or(Error::Synthesis),
|
||||
|| lambda_2,
|
||||
)?;
|
||||
|
||||
lambda_2
|
||||
@@ -390,28 +353,21 @@ where
|
||||
|
||||
// Compute and assign `x_a` for the next row.
|
||||
let x_a_new: X<pallas::Base> = {
|
||||
let x_a_new = lambda_2
|
||||
.zip(x_a.value())
|
||||
.zip(x_r)
|
||||
.map(|((lambda_2, x_a), x_r)| lambda_2.square() - x_a - x_r);
|
||||
let x_a_new = lambda_2.square() - x_a.value() - x_r;
|
||||
|
||||
let x_a_cell = region.assign_advice(
|
||||
|| "x_a",
|
||||
config.double_and_add.x_a,
|
||||
offset + row + 1,
|
||||
|| x_a_new.ok_or(Error::Synthesis),
|
||||
|| x_a_new,
|
||||
)?;
|
||||
|
||||
x_a_cell.into()
|
||||
};
|
||||
|
||||
// Compute y_a for the next row.
|
||||
let y_a_new: Y<pallas::Base> = lambda_2
|
||||
.zip(x_a.value())
|
||||
.zip(x_a_new.value())
|
||||
.zip(y_a.0)
|
||||
.map(|(((lambda_2, x_a), x_a_new), y_a)| lambda_2 * (x_a - x_a_new) - y_a)
|
||||
.into();
|
||||
let y_a_new: Y<pallas::Base> =
|
||||
(lambda_2 * (x_a.value() - x_a_new.value()) - y_a.0).into();
|
||||
|
||||
// Update the mutable `x_a`, `y_a` variables.
|
||||
x_a = x_a_new;
|
||||
@@ -444,18 +400,18 @@ impl<F: FieldExt> Deref for X<F> {
|
||||
/// This is never actually witnessed until the last round, since it
|
||||
/// can be derived from other variables. Thus it only exists as a field
|
||||
/// element, not a `CellValue`.
|
||||
struct Y<F: FieldExt>(Option<Assigned<F>>);
|
||||
struct Y<F: FieldExt>(Value<Assigned<F>>);
|
||||
|
||||
impl<F: FieldExt> From<Option<Assigned<F>>> for Y<F> {
|
||||
fn from(value: Option<Assigned<F>>) -> Self {
|
||||
impl<F: FieldExt> From<Value<Assigned<F>>> for Y<F> {
|
||||
fn from(value: Value<Assigned<F>>) -> Self {
|
||||
Y(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FieldExt> Deref for Y<F> {
|
||||
type Target = Option<Assigned<F>>;
|
||||
type Target = Value<Assigned<F>>;
|
||||
|
||||
fn deref(&self) -> &Option<Assigned<F>> {
|
||||
fn deref(&self) -> &Value<Assigned<F>> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
//! Gadgets for implementing a Merkle tree with Sinsemilla.
|
||||
|
||||
use halo2_proofs::{
|
||||
circuit::{Chip, Layouter},
|
||||
circuit::{Chip, Layouter, Value},
|
||||
plonk::Error,
|
||||
};
|
||||
use pasta_curves::arithmetic::CurveAffine;
|
||||
|
||||
use super::{HashDomains, SinsemillaInstructions};
|
||||
|
||||
use crate::utilities::{
|
||||
cond_swap::CondSwapInstructions, i2lebsp, transpose_option_array, UtilitiesInstructions,
|
||||
};
|
||||
use crate::utilities::{cond_swap::CondSwapInstructions, i2lebsp, UtilitiesInstructions};
|
||||
|
||||
pub mod chip;
|
||||
|
||||
@@ -60,9 +58,9 @@ pub struct MerklePath<
|
||||
{
|
||||
chips: [MerkleChip; PAR],
|
||||
domain: MerkleChip::HashDomains,
|
||||
leaf_pos: Option<u32>,
|
||||
leaf_pos: Value<u32>,
|
||||
// The Merkle path is ordered from leaves to root.
|
||||
path: Option<[C::Base; PATH_LENGTH]>,
|
||||
path: Value<[C::Base; PATH_LENGTH]>,
|
||||
}
|
||||
|
||||
impl<
|
||||
@@ -86,8 +84,8 @@ where
|
||||
pub fn construct(
|
||||
chips: [MerkleChip; PAR],
|
||||
domain: MerkleChip::HashDomains,
|
||||
leaf_pos: Option<u32>,
|
||||
path: Option<[C::Base; PATH_LENGTH]>,
|
||||
leaf_pos: Value<u32>,
|
||||
path: Value<[C::Base; PATH_LENGTH]>,
|
||||
) -> Self {
|
||||
assert_ne!(PAR, 0);
|
||||
Self {
|
||||
@@ -129,12 +127,12 @@ where
|
||||
|
||||
// The Merkle path is ordered from leaves to root, which is consistent with the
|
||||
// little-endian representation of `pos` below.
|
||||
let path = transpose_option_array(self.path);
|
||||
let path = self.path.transpose_array();
|
||||
|
||||
// Get position as a PATH_LENGTH-bit bitstring (little-endian bit order).
|
||||
let pos: [Option<bool>; PATH_LENGTH] = {
|
||||
let pos: Option<[bool; PATH_LENGTH]> = self.leaf_pos.map(|pos| i2lebsp(pos as u64));
|
||||
transpose_option_array(pos)
|
||||
let pos: [Value<bool>; PATH_LENGTH] = {
|
||||
let pos: Value<[bool; PATH_LENGTH]> = self.leaf_pos.map(|pos| i2lebsp(pos as u64));
|
||||
pos.transpose_array()
|
||||
};
|
||||
|
||||
let Q = self.domain.Q();
|
||||
@@ -191,7 +189,7 @@ pub mod tests {
|
||||
|
||||
use group::ff::{Field, PrimeField, PrimeFieldBits};
|
||||
use halo2_proofs::{
|
||||
circuit::{Layouter, SimpleFloorPlanner},
|
||||
circuit::{Layouter, SimpleFloorPlanner, Value},
|
||||
dev::MockProver,
|
||||
pasta::pallas,
|
||||
plonk::{Circuit, ConstraintSystem, Error},
|
||||
@@ -204,9 +202,9 @@ pub mod tests {
|
||||
|
||||
#[derive(Default)]
|
||||
struct MyCircuit {
|
||||
leaf: Option<pallas::Base>,
|
||||
leaf_pos: Option<u32>,
|
||||
merkle_path: Option<[pallas::Base; MERKLE_DEPTH]>,
|
||||
leaf: Value<pallas::Base>,
|
||||
leaf_pos: Value<u32>,
|
||||
merkle_path: Value<[pallas::Base; MERKLE_DEPTH]>,
|
||||
}
|
||||
|
||||
impl Circuit<pallas::Base> for MyCircuit {
|
||||
@@ -306,46 +304,52 @@ pub mod tests {
|
||||
let computed_final_root =
|
||||
path.calculate_root(layouter.namespace(|| "calculate root"), leaf)?;
|
||||
|
||||
if let Some(leaf_pos) = self.leaf_pos {
|
||||
// The expected final root
|
||||
let final_root = self.merkle_path.unwrap().iter().enumerate().fold(
|
||||
self.leaf.unwrap(),
|
||||
|node, (l, sibling)| {
|
||||
let l = l as u8;
|
||||
let (left, right) = if leaf_pos & (1 << l) == 0 {
|
||||
(&node, sibling)
|
||||
} else {
|
||||
(sibling, &node)
|
||||
};
|
||||
self.leaf
|
||||
.zip(self.leaf_pos)
|
||||
.zip(self.merkle_path)
|
||||
.zip(computed_final_root.value())
|
||||
.assert_if_known(|(((leaf, leaf_pos), merkle_path), computed_final_root)| {
|
||||
// The expected final root
|
||||
let final_root =
|
||||
merkle_path
|
||||
.iter()
|
||||
.enumerate()
|
||||
.fold(*leaf, |node, (l, sibling)| {
|
||||
let l = l as u8;
|
||||
let (left, right) = if leaf_pos & (1 << l) == 0 {
|
||||
(&node, sibling)
|
||||
} else {
|
||||
(sibling, &node)
|
||||
};
|
||||
|
||||
use crate::sinsemilla::primitives as sinsemilla;
|
||||
let merkle_crh = sinsemilla::HashDomain::from_Q(TestHashDomain.Q().into());
|
||||
use crate::sinsemilla::primitives as sinsemilla;
|
||||
let merkle_crh =
|
||||
sinsemilla::HashDomain::from_Q(TestHashDomain.Q().into());
|
||||
|
||||
merkle_crh
|
||||
.hash(
|
||||
iter::empty()
|
||||
.chain(i2lebsp::<10>(l as u64).iter().copied())
|
||||
.chain(
|
||||
left.to_le_bits()
|
||||
.iter()
|
||||
.by_vals()
|
||||
.take(pallas::Base::NUM_BITS as usize),
|
||||
merkle_crh
|
||||
.hash(
|
||||
iter::empty()
|
||||
.chain(i2lebsp::<10>(l as u64).iter().copied())
|
||||
.chain(
|
||||
left.to_le_bits()
|
||||
.iter()
|
||||
.by_vals()
|
||||
.take(pallas::Base::NUM_BITS as usize),
|
||||
)
|
||||
.chain(
|
||||
right
|
||||
.to_le_bits()
|
||||
.iter()
|
||||
.by_vals()
|
||||
.take(pallas::Base::NUM_BITS as usize),
|
||||
),
|
||||
)
|
||||
.chain(
|
||||
right
|
||||
.to_le_bits()
|
||||
.iter()
|
||||
.by_vals()
|
||||
.take(pallas::Base::NUM_BITS as usize),
|
||||
),
|
||||
)
|
||||
.unwrap_or(pallas::Base::zero())
|
||||
},
|
||||
);
|
||||
.unwrap_or(pallas::Base::zero())
|
||||
});
|
||||
|
||||
// Check the computed final root against the expected final root.
|
||||
assert_eq!(computed_final_root.value().unwrap(), &final_root);
|
||||
}
|
||||
// Check the computed final root against the expected final root.
|
||||
computed_final_root == &&final_root
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -367,9 +371,9 @@ pub mod tests {
|
||||
// The root is provided as a public input in the Orchard circuit.
|
||||
|
||||
let circuit = MyCircuit {
|
||||
leaf: Some(leaf),
|
||||
leaf_pos: Some(pos),
|
||||
merkle_path: Some(path.try_into().unwrap()),
|
||||
leaf: Value::known(leaf),
|
||||
leaf_pos: Value::known(pos),
|
||||
merkle_path: Value::known(path.try_into().unwrap()),
|
||||
};
|
||||
|
||||
let prover = MockProver::run(11, &circuit, vec![]).unwrap();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Chip implementing a Merkle hash using Sinsemilla as the hash function.
|
||||
|
||||
use halo2_proofs::{
|
||||
circuit::{AssignedCell, Chip, Layouter},
|
||||
circuit::{AssignedCell, Chip, Layouter, Value},
|
||||
plonk::{Advice, Column, ConstraintSystem, Constraints, Error, Selector},
|
||||
poly::Rotation,
|
||||
};
|
||||
@@ -234,7 +234,7 @@ where
|
||||
self.clone(),
|
||||
layouter.namespace(|| "Witness a = a_0 || a_1"),
|
||||
[
|
||||
RangeConstrained::bitrange_of(Some(&pallas::Base::from(l as u64)), 0..10),
|
||||
RangeConstrained::bitrange_of(Value::known(&pallas::Base::from(l as u64)), 0..10),
|
||||
RangeConstrained::bitrange_of(left.value(), 0..240),
|
||||
],
|
||||
)?;
|
||||
@@ -382,30 +382,33 @@ where
|
||||
|
||||
use group::ff::PrimeFieldBits;
|
||||
|
||||
if let (Some(left), Some(right)) = (left.value(), right.value()) {
|
||||
let l = i2lebsp::<10>(l as u64);
|
||||
let left: Vec<_> = left
|
||||
.to_le_bits()
|
||||
.iter()
|
||||
.by_vals()
|
||||
.take(pallas::Base::NUM_BITS as usize)
|
||||
.collect();
|
||||
let right: Vec<_> = right
|
||||
.to_le_bits()
|
||||
.iter()
|
||||
.by_vals()
|
||||
.take(pallas::Base::NUM_BITS as usize)
|
||||
.collect();
|
||||
let merkle_crh = HashDomain::from_Q(Q.into());
|
||||
left.value()
|
||||
.zip(right.value())
|
||||
.zip(hash.value())
|
||||
.assert_if_known(|((left, right), hash)| {
|
||||
let l = i2lebsp::<10>(l as u64);
|
||||
let left: Vec<_> = left
|
||||
.to_le_bits()
|
||||
.iter()
|
||||
.by_vals()
|
||||
.take(pallas::Base::NUM_BITS as usize)
|
||||
.collect();
|
||||
let right: Vec<_> = right
|
||||
.to_le_bits()
|
||||
.iter()
|
||||
.by_vals()
|
||||
.take(pallas::Base::NUM_BITS as usize)
|
||||
.collect();
|
||||
let merkle_crh = HashDomain::from_Q(Q.into());
|
||||
|
||||
let mut message = l.to_vec();
|
||||
message.extend_from_slice(&left);
|
||||
message.extend_from_slice(&right);
|
||||
let mut message = l.to_vec();
|
||||
message.extend_from_slice(&left);
|
||||
message.extend_from_slice(&right);
|
||||
|
||||
let expected = merkle_crh.hash(message.into_iter()).unwrap();
|
||||
let expected = merkle_crh.hash(message.into_iter()).unwrap();
|
||||
|
||||
assert_eq!(expected.to_repr(), hash.value().unwrap().to_repr());
|
||||
}
|
||||
expected.to_repr() == hash.to_repr()
|
||||
});
|
||||
}
|
||||
|
||||
Ok(hash)
|
||||
@@ -431,8 +434,8 @@ where
|
||||
fn swap(
|
||||
&self,
|
||||
layouter: impl Layouter<pallas::Base>,
|
||||
pair: (Self::Var, Option<pallas::Base>),
|
||||
swap: Option<bool>,
|
||||
pair: (Self::Var, Value<pallas::Base>),
|
||||
swap: Value<bool>,
|
||||
) -> Result<(Self::Var, Self::Var), Error> {
|
||||
let config = self.config().cond_swap_config.clone();
|
||||
let chip = CondSwapChip::<pallas::Base>::construct(config);
|
||||
@@ -499,7 +502,7 @@ where
|
||||
fn witness_message_piece(
|
||||
&self,
|
||||
layouter: impl Layouter<pallas::Base>,
|
||||
value: Option<pallas::Base>,
|
||||
value: Value<pallas::Base>,
|
||||
num_words: usize,
|
||||
) -> Result<Self::MessagePiece, Error> {
|
||||
let config = self.config().sinsemilla_config.clone();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
use ff::PrimeFieldBits;
|
||||
use halo2_proofs::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::{AssignedCell, Cell},
|
||||
circuit::{AssignedCell, Cell, Value},
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
|
||||
@@ -58,7 +58,7 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> MessagePiece<F, K> {
|
||||
self.cell_value.cell()
|
||||
}
|
||||
|
||||
pub fn field_elem(&self) -> Option<F> {
|
||||
pub fn field_elem(&self) -> Value<F> {
|
||||
self.cell_value.value().cloned()
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use ff::{Field, PrimeFieldBits};
|
||||
use halo2_proofs::{
|
||||
circuit::{AssignedCell, Cell, Layouter},
|
||||
circuit::{AssignedCell, Cell, Layouter, Value},
|
||||
plonk::{Advice, Column, Error, Expression},
|
||||
};
|
||||
use pasta_curves::arithmetic::FieldExt;
|
||||
@@ -16,17 +16,17 @@ pub mod lookup_range_check;
|
||||
/// A type that has a value at either keygen or proving time.
|
||||
pub trait FieldValue<F: Field> {
|
||||
/// Returns the value of this type.
|
||||
fn value(&self) -> Option<&F>;
|
||||
fn value(&self) -> Value<&F>;
|
||||
}
|
||||
|
||||
impl<F: Field> FieldValue<F> for Option<F> {
|
||||
fn value(&self) -> Option<&F> {
|
||||
impl<F: Field> FieldValue<F> for Value<F> {
|
||||
fn value(&self) -> Value<&F> {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> FieldValue<F> for AssignedCell<F, F> {
|
||||
fn value(&self) -> Option<&F> {
|
||||
fn value(&self) -> Value<&F> {
|
||||
self.value()
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ pub trait Var<F: FieldExt>: Clone + std::fmt::Debug + From<AssignedCell<F, F>> {
|
||||
fn cell(&self) -> Cell;
|
||||
|
||||
/// The value allocated to this variable.
|
||||
fn value(&self) -> Option<F>;
|
||||
fn value(&self) -> Value<F>;
|
||||
}
|
||||
|
||||
impl<F: FieldExt> Var<F> for AssignedCell<F, F> {
|
||||
@@ -45,7 +45,7 @@ impl<F: FieldExt> Var<F> for AssignedCell<F, F> {
|
||||
self.cell()
|
||||
}
|
||||
|
||||
fn value(&self) -> Option<F> {
|
||||
fn value(&self) -> Value<F> {
|
||||
self.value().cloned()
|
||||
}
|
||||
}
|
||||
@@ -60,18 +60,13 @@ pub trait UtilitiesInstructions<F: FieldExt> {
|
||||
&self,
|
||||
mut layouter: impl Layouter<F>,
|
||||
column: Column<Advice>,
|
||||
value: Option<F>,
|
||||
value: Value<F>,
|
||||
) -> Result<Self::Var, Error> {
|
||||
layouter.assign_region(
|
||||
|| "load private",
|
||||
|mut region| {
|
||||
region
|
||||
.assign_advice(
|
||||
|| "load private",
|
||||
column,
|
||||
0,
|
||||
|| value.ok_or(Error::Synthesis),
|
||||
)
|
||||
.assign_advice(|| "load private", column, 0, || value)
|
||||
.map(Self::Var::from)
|
||||
},
|
||||
)
|
||||
@@ -98,9 +93,9 @@ impl<F: Field, T: FieldValue<F>> RangeConstrained<F, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: PrimeFieldBits> RangeConstrained<F, Option<F>> {
|
||||
/// Constructs a `RangeConstrained<Option<F>>` as a bitrange of the given value.
|
||||
pub fn bitrange_of(value: Option<&F>, bitrange: Range<usize>) -> Self {
|
||||
impl<F: PrimeFieldBits> RangeConstrained<F, Value<F>> {
|
||||
/// Constructs a `RangeConstrained<Value<F>>` as a bitrange of the given value.
|
||||
pub fn bitrange_of(value: Value<&F>, bitrange: Range<usize>) -> Self {
|
||||
let num_bits = bitrange.len();
|
||||
Self {
|
||||
inner: value.map(|value| bitrange_subset(value, bitrange)),
|
||||
@@ -125,7 +120,7 @@ impl<F: Field> RangeConstrained<F, AssignedCell<F, F>> {
|
||||
}
|
||||
|
||||
/// Extracts the range-constrained value from this range-constrained cell.
|
||||
pub fn value(&self) -> RangeConstrained<F, Option<F>> {
|
||||
pub fn value(&self) -> RangeConstrained<F, Value<F>> {
|
||||
RangeConstrained {
|
||||
inner: self.inner.value().copied(),
|
||||
num_bits: self.num_bits,
|
||||
@@ -134,18 +129,6 @@ impl<F: Field> RangeConstrained<F, AssignedCell<F, F>> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn transpose_option_array<T: Copy + std::fmt::Debug, const LEN: usize>(
|
||||
option_array: Option<[T; LEN]>,
|
||||
) -> [Option<T>; LEN] {
|
||||
let mut ret = [None; LEN];
|
||||
if let Some(arr) = option_array {
|
||||
for (entry, value) in ret.iter_mut().zip(arr) {
|
||||
*entry = Some(value);
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
/// Checks that an expression is either 1 or 0.
|
||||
pub fn bool_check<F: FieldExt>(value: Expression<F>) -> Expression<F> {
|
||||
range_check(value, 2)
|
||||
@@ -320,7 +303,7 @@ mod tests {
|
||||
|| format!("witness {}", self.0),
|
||||
config.advice,
|
||||
0,
|
||||
|| Ok(pallas::Base::from(self.0 as u64)),
|
||||
|| Value::known(pallas::Base::from(self.0 as u64)),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use super::{bool_check, ternary, UtilitiesInstructions};
|
||||
use halo2_proofs::{
|
||||
circuit::{AssignedCell, Chip, Layouter},
|
||||
circuit::{AssignedCell, Chip, Layouter, Value},
|
||||
plonk::{Advice, Column, ConstraintSystem, Constraints, Error, Selector},
|
||||
poly::Rotation,
|
||||
};
|
||||
@@ -20,8 +20,8 @@ pub trait CondSwapInstructions<F: FieldExt>: UtilitiesInstructions<F> {
|
||||
fn swap(
|
||||
&self,
|
||||
layouter: impl Layouter<F>,
|
||||
pair: (Self::Var, Option<F>),
|
||||
swap: Option<bool>,
|
||||
pair: (Self::Var, Value<F>),
|
||||
swap: Value<bool>,
|
||||
) -> Result<(Self::Var, Self::Var), Error>;
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ impl<F: FieldExt> CondSwapInstructions<F> for CondSwapChip<F> {
|
||||
fn swap(
|
||||
&self,
|
||||
mut layouter: impl Layouter<F>,
|
||||
pair: (Self::Var, Option<F>),
|
||||
swap: Option<bool>,
|
||||
pair: (Self::Var, Value<F>),
|
||||
swap: Value<bool>,
|
||||
) -> Result<(Self::Var, Self::Var), Error> {
|
||||
let config = self.config();
|
||||
|
||||
@@ -87,21 +87,11 @@ impl<F: FieldExt> CondSwapInstructions<F> for CondSwapChip<F> {
|
||||
let a = pair.0.copy_advice(|| "copy a", &mut region, config.a, 0)?;
|
||||
|
||||
// Witness `b` value
|
||||
let b = region.assign_advice(
|
||||
|| "witness b",
|
||||
config.b,
|
||||
0,
|
||||
|| pair.1.ok_or(Error::Synthesis),
|
||||
)?;
|
||||
let b = region.assign_advice(|| "witness b", config.b, 0, || pair.1)?;
|
||||
|
||||
// Witness `swap` value
|
||||
let swap_val = swap.map(|swap| F::from(swap as u64));
|
||||
region.assign_advice(
|
||||
|| "swap",
|
||||
config.swap,
|
||||
0,
|
||||
|| swap_val.ok_or(Error::Synthesis),
|
||||
)?;
|
||||
region.assign_advice(|| "swap", config.swap, 0, || swap_val)?;
|
||||
|
||||
// Conditionally swap a
|
||||
let a_swapped = {
|
||||
@@ -111,12 +101,7 @@ impl<F: FieldExt> CondSwapInstructions<F> for CondSwapChip<F> {
|
||||
.zip(swap)
|
||||
.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),
|
||||
)?
|
||||
region.assign_advice(|| "a_swapped", config.a_swapped, 0, || a_swapped)?
|
||||
};
|
||||
|
||||
// Conditionally swap b
|
||||
@@ -127,12 +112,7 @@ impl<F: FieldExt> CondSwapInstructions<F> for CondSwapChip<F> {
|
||||
.zip(swap)
|
||||
.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),
|
||||
)?
|
||||
region.assign_advice(|| "b_swapped", config.b_swapped, 0, || b_swapped)?
|
||||
};
|
||||
|
||||
// Return swapped pair
|
||||
@@ -217,7 +197,7 @@ mod tests {
|
||||
use super::{CondSwapChip, CondSwapConfig, CondSwapInstructions};
|
||||
use group::ff::Field;
|
||||
use halo2_proofs::{
|
||||
circuit::{Layouter, SimpleFloorPlanner},
|
||||
circuit::{Layouter, SimpleFloorPlanner, Value},
|
||||
dev::MockProver,
|
||||
plonk::{Circuit, ConstraintSystem, Error},
|
||||
};
|
||||
@@ -228,9 +208,9 @@ mod tests {
|
||||
fn cond_swap() {
|
||||
#[derive(Default)]
|
||||
struct MyCircuit<F: FieldExt> {
|
||||
a: Option<F>,
|
||||
b: Option<F>,
|
||||
swap: Option<bool>,
|
||||
a: Value<F>,
|
||||
b: Value<F>,
|
||||
swap: Value<bool>,
|
||||
}
|
||||
|
||||
impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
|
||||
@@ -269,17 +249,18 @@ mod tests {
|
||||
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());
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
self.swap
|
||||
.zip(a.value().zip(self.b.as_ref()))
|
||||
.zip(swapped_pair.0.value().zip(swapped_pair.1.value()))
|
||||
.assert_if_known(|((swap, (a, b)), (a_swapped, b_swapped))| {
|
||||
if *swap {
|
||||
// Check that `a` and `b` have been swapped
|
||||
(a_swapped == b) && (b_swapped == a)
|
||||
} else {
|
||||
// Check that `a` and `b` have not been swapped
|
||||
(a_swapped == a) && (b_swapped == b)
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -290,9 +271,9 @@ mod tests {
|
||||
// Test swap case
|
||||
{
|
||||
let circuit: MyCircuit<Base> = MyCircuit {
|
||||
a: Some(Base::random(rng)),
|
||||
b: Some(Base::random(rng)),
|
||||
swap: Some(true),
|
||||
a: Value::known(Base::random(rng)),
|
||||
b: Value::known(Base::random(rng)),
|
||||
swap: Value::known(true),
|
||||
};
|
||||
let prover = MockProver::<Base>::run(3, &circuit, vec![]).unwrap();
|
||||
assert_eq!(prover.verify(), Ok(()));
|
||||
@@ -301,9 +282,9 @@ mod tests {
|
||||
// Test non-swap case
|
||||
{
|
||||
let circuit: MyCircuit<Base> = MyCircuit {
|
||||
a: Some(Base::random(rng)),
|
||||
b: Some(Base::random(rng)),
|
||||
swap: Some(false),
|
||||
a: Value::known(Base::random(rng)),
|
||||
b: Value::known(Base::random(rng)),
|
||||
swap: Value::known(false),
|
||||
};
|
||||
let prover = MockProver::<Base>::run(3, &circuit, vec![]).unwrap();
|
||||
assert_eq!(prover.verify(), Ok(()));
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
use ff::PrimeFieldBits;
|
||||
use halo2_proofs::{
|
||||
circuit::{AssignedCell, Region},
|
||||
circuit::{AssignedCell, Region, Value},
|
||||
plonk::{Advice, Column, ConstraintSystem, Constraints, Error, Selector},
|
||||
poly::Rotation,
|
||||
};
|
||||
@@ -107,17 +107,12 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
|
||||
&self,
|
||||
region: &mut Region<'_, F>,
|
||||
offset: usize,
|
||||
alpha: Option<F>,
|
||||
alpha: Value<F>,
|
||||
strict: bool,
|
||||
word_num_bits: usize,
|
||||
num_windows: usize,
|
||||
) -> Result<RunningSum<F>, Error> {
|
||||
let z_0 = region.assign_advice(
|
||||
|| "z_0 = alpha",
|
||||
self.z,
|
||||
offset,
|
||||
|| alpha.ok_or(Error::Synthesis),
|
||||
)?;
|
||||
let z_0 = region.assign_advice(|| "z_0 = alpha", self.z, offset, || alpha)?;
|
||||
self.decompose(region, offset, z_0, strict, word_num_bits, num_windows)
|
||||
}
|
||||
|
||||
@@ -170,17 +165,10 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
|
||||
}
|
||||
|
||||
// Decompose base field element into K-bit words.
|
||||
let words: Vec<Option<u8>> = {
|
||||
let words = z_0
|
||||
.value()
|
||||
.map(|word| super::decompose_word::<F>(word, word_num_bits, WINDOW_NUM_BITS));
|
||||
|
||||
if let Some(words) = words {
|
||||
words.into_iter().map(Some).collect()
|
||||
} else {
|
||||
vec![None; num_windows]
|
||||
}
|
||||
};
|
||||
let words = z_0
|
||||
.value()
|
||||
.map(|word| super::decompose_word::<F>(word, word_num_bits, WINDOW_NUM_BITS))
|
||||
.transpose_vec(num_windows);
|
||||
|
||||
// Initialize empty vector to store running sum values [z_0, ..., z_W].
|
||||
let mut zs: Vec<AssignedCell<F, F>> = vec![z_0.clone()];
|
||||
@@ -189,20 +177,18 @@ impl<F: FieldExt + PrimeFieldBits, const WINDOW_NUM_BITS: usize>
|
||||
// Assign running sum `z_{i+1}` = (z_i - k_i) / (2^K) for i = 0..=n-1.
|
||||
// Outside of this helper, z_0 = alpha must have already been loaded into the
|
||||
// `z` column at `offset`.
|
||||
let two_pow_k_inv = F::from(1 << WINDOW_NUM_BITS as u64).invert().unwrap();
|
||||
let two_pow_k_inv = Value::known(F::from(1 << WINDOW_NUM_BITS as u64).invert().unwrap());
|
||||
for (i, word) in words.iter().enumerate() {
|
||||
// z_next = (z_cur - word) / (2^K)
|
||||
let z_next = {
|
||||
let z_cur_val = z.value().copied();
|
||||
let word = word.map(|word| F::from(word as u64));
|
||||
let z_next_val = z
|
||||
.value()
|
||||
.zip(word)
|
||||
.map(|(z_cur_val, word)| (*z_cur_val - word) * two_pow_k_inv);
|
||||
let z_next_val = (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),
|
||||
|| z_next_val,
|
||||
)?
|
||||
};
|
||||
|
||||
@@ -247,7 +233,7 @@ mod tests {
|
||||
const WINDOW_NUM_BITS: usize,
|
||||
const NUM_WINDOWS: usize,
|
||||
> {
|
||||
alpha: Option<F>,
|
||||
alpha: Value<F>,
|
||||
strict: bool,
|
||||
}
|
||||
|
||||
@@ -263,7 +249,7 @@ mod tests {
|
||||
|
||||
fn without_witnesses(&self) -> Self {
|
||||
Self {
|
||||
alpha: None,
|
||||
alpha: Value::unknown(),
|
||||
strict: self.strict,
|
||||
}
|
||||
}
|
||||
@@ -320,7 +306,7 @@ mod tests {
|
||||
// Strict full decomposition should pass.
|
||||
let circuit: MyCircuit<pallas::Base, L_BASE, FIXED_BASE_WINDOW_SIZE, { NUM_WINDOWS }> =
|
||||
MyCircuit {
|
||||
alpha: Some(alpha),
|
||||
alpha: Value::known(alpha),
|
||||
strict: true,
|
||||
};
|
||||
let prover = MockProver::<pallas::Base>::run(8, &circuit, vec![]).unwrap();
|
||||
@@ -338,7 +324,7 @@ mod tests {
|
||||
FIXED_BASE_WINDOW_SIZE,
|
||||
{ NUM_WINDOWS_SHORT },
|
||||
> = MyCircuit {
|
||||
alpha: Some(alpha),
|
||||
alpha: Value::known(alpha),
|
||||
strict: true,
|
||||
};
|
||||
let prover = MockProver::<pallas::Base>::run(8, &circuit, vec![]).unwrap();
|
||||
@@ -356,7 +342,7 @@ mod tests {
|
||||
FIXED_BASE_WINDOW_SIZE,
|
||||
{ NUM_WINDOWS_SHORT },
|
||||
> = MyCircuit {
|
||||
alpha: Some(alpha),
|
||||
alpha: Value::known(alpha),
|
||||
strict: true,
|
||||
};
|
||||
let prover = MockProver::<pallas::Base>::run(8, &circuit, vec![]).unwrap();
|
||||
@@ -395,7 +381,7 @@ mod tests {
|
||||
FIXED_BASE_WINDOW_SIZE,
|
||||
{ NUM_WINDOWS_SHORT },
|
||||
> = MyCircuit {
|
||||
alpha: Some(alpha),
|
||||
alpha: Value::known(alpha),
|
||||
strict: false,
|
||||
};
|
||||
let prover = MockProver::<pallas::Base>::run(8, &circuit, vec![]).unwrap();
|
||||
|
||||
@@ -33,7 +33,7 @@ impl<F: FieldExt + PrimeFieldBits> RangeConstrained<F, AssignedCell<F, F>> {
|
||||
pub fn witness_short<const K: usize>(
|
||||
lookup_config: &LookupRangeCheckConfig<F, K>,
|
||||
layouter: impl Layouter<F>,
|
||||
value: Option<&F>,
|
||||
value: Value<&F>,
|
||||
bitrange: Range<usize>,
|
||||
) -> Result<Self, Error> {
|
||||
let num_bits = bitrange.len();
|
||||
@@ -165,7 +165,7 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
|
||||
|| "table_idx",
|
||||
self.table_idx,
|
||||
index,
|
||||
|| Ok(F::from(index as u64)),
|
||||
|| Value::known(F::from(index as u64)),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
@@ -198,19 +198,15 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
|
||||
pub fn witness_check(
|
||||
&self,
|
||||
mut layouter: impl Layouter<F>,
|
||||
value: Option<F>,
|
||||
value: Value<F>,
|
||||
num_words: usize,
|
||||
strict: bool,
|
||||
) -> Result<RunningSum<F>, Error> {
|
||||
layouter.assign_region(
|
||||
|| "Witness element",
|
||||
|mut region| {
|
||||
let z_0 = region.assign_advice(
|
||||
|| "Witness element",
|
||||
self.running_sum,
|
||||
0,
|
||||
|| value.ok_or(Error::Synthesis),
|
||||
)?;
|
||||
let z_0 =
|
||||
region.assign_advice(|| "Witness element", self.running_sum, 0, || value)?;
|
||||
self.range_check(&mut region, z_0, num_words, strict)
|
||||
},
|
||||
)
|
||||
@@ -245,16 +241,12 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
let words: Option<Vec<F>> = bits.map(|bits| {
|
||||
bits.map(|bits| {
|
||||
bits.chunks_exact(K)
|
||||
.map(|word| F::from(lebs2ip::<K>(&(word.try_into().unwrap()))))
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
if let Some(words) = words {
|
||||
words.into_iter().map(Some).collect()
|
||||
} else {
|
||||
vec![None; num_words]
|
||||
}
|
||||
})
|
||||
.transpose_vec(num_words)
|
||||
};
|
||||
|
||||
let mut zs = vec![element.clone()];
|
||||
@@ -285,7 +277,7 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
|
||||
|| format!("z_{:?}", idx + 1),
|
||||
self.running_sum,
|
||||
idx + 1,
|
||||
|| z_val.ok_or(Error::Synthesis),
|
||||
|| z_val,
|
||||
)?
|
||||
};
|
||||
zs.push(z.clone());
|
||||
@@ -331,7 +323,7 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
|
||||
pub fn witness_short_check(
|
||||
&self,
|
||||
mut layouter: impl Layouter<F>,
|
||||
element: Option<F>,
|
||||
element: Value<F>,
|
||||
num_bits: usize,
|
||||
) -> Result<AssignedCell<F, F>, Error> {
|
||||
assert!(num_bits <= K);
|
||||
@@ -339,12 +331,8 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
|
||||
|| format!("Range check {:?} bits", num_bits),
|
||||
|mut region| {
|
||||
// Witness `element` to use in the k-bit lookup.
|
||||
let element = region.assign_advice(
|
||||
|| "Witness element",
|
||||
self.running_sum,
|
||||
0,
|
||||
|| element.ok_or(Error::Synthesis),
|
||||
)?;
|
||||
let element =
|
||||
region.assign_advice(|| "Witness element", self.running_sum, 0, || element)?;
|
||||
|
||||
self.short_range_check(&mut region, element.clone(), num_bits)?;
|
||||
|
||||
@@ -372,16 +360,13 @@ impl<F: FieldExt + PrimeFieldBits, const K: usize> LookupRangeCheckConfig<F, K>
|
||||
self.q_bitshift.enable(region, 1)?;
|
||||
|
||||
// Assign shifted `element * 2^{K - num_bits}`
|
||||
let shifted = element.value().map(|element| {
|
||||
let shift = F::from(1 << (K - num_bits));
|
||||
*element * shift
|
||||
});
|
||||
let shifted = element.value().into_field() * F::from(1 << (K - num_bits));
|
||||
|
||||
region.assign_advice(
|
||||
|| format!("element * 2^({}-{})", K, num_bits),
|
||||
self.running_sum,
|
||||
1,
|
||||
|| shifted.ok_or(Error::Synthesis),
|
||||
|| shifted,
|
||||
)?;
|
||||
|
||||
// Assign 2^{-num_bits} from a fixed column.
|
||||
@@ -406,7 +391,7 @@ mod tests {
|
||||
|
||||
use ff::{Field, PrimeFieldBits};
|
||||
use halo2_proofs::{
|
||||
circuit::{Layouter, SimpleFloorPlanner},
|
||||
circuit::{Layouter, SimpleFloorPlanner, Value},
|
||||
dev::{FailureLocation, MockProver, VerifyFailure},
|
||||
plonk::{Circuit, ConstraintSystem, Error},
|
||||
};
|
||||
@@ -485,7 +470,7 @@ mod tests {
|
||||
|
||||
let zs = config.witness_check(
|
||||
layouter.namespace(|| format!("Lookup {:?}", self.num_words)),
|
||||
Some(*element),
|
||||
Value::known(*element),
|
||||
self.num_words,
|
||||
*strict,
|
||||
)?;
|
||||
@@ -493,9 +478,7 @@ mod tests {
|
||||
assert_eq!(*expected_zs.last().unwrap(), *expected_final_z);
|
||||
|
||||
for (expected_z, z) in expected_zs.into_iter().zip(zs.iter()) {
|
||||
if let Some(z) = z.value() {
|
||||
assert_eq!(&expected_z, z);
|
||||
}
|
||||
z.value().assert_if_known(|z| &&expected_z == z);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -516,7 +499,7 @@ mod tests {
|
||||
#[test]
|
||||
fn short_range_check() {
|
||||
struct MyCircuit<F: FieldExt + PrimeFieldBits> {
|
||||
element: Option<F>,
|
||||
element: Value<F>,
|
||||
num_bits: usize,
|
||||
}
|
||||
|
||||
@@ -526,7 +509,7 @@ mod tests {
|
||||
|
||||
fn without_witnesses(&self) -> Self {
|
||||
MyCircuit {
|
||||
element: None,
|
||||
element: Value::unknown(),
|
||||
num_bits: self.num_bits,
|
||||
}
|
||||
}
|
||||
@@ -562,7 +545,7 @@ mod tests {
|
||||
// Edge case: zero bits
|
||||
{
|
||||
let circuit: MyCircuit<pallas::Base> = MyCircuit {
|
||||
element: Some(pallas::Base::zero()),
|
||||
element: Value::known(pallas::Base::zero()),
|
||||
num_bits: 0,
|
||||
};
|
||||
let prover = MockProver::<pallas::Base>::run(11, &circuit, vec![]).unwrap();
|
||||
@@ -572,7 +555,7 @@ mod tests {
|
||||
// Edge case: K bits
|
||||
{
|
||||
let circuit: MyCircuit<pallas::Base> = MyCircuit {
|
||||
element: Some(pallas::Base::from((1 << K) - 1)),
|
||||
element: Value::known(pallas::Base::from((1 << K) - 1)),
|
||||
num_bits: K,
|
||||
};
|
||||
let prover = MockProver::<pallas::Base>::run(11, &circuit, vec![]).unwrap();
|
||||
@@ -582,7 +565,7 @@ mod tests {
|
||||
// Element within `num_bits`
|
||||
{
|
||||
let circuit: MyCircuit<pallas::Base> = MyCircuit {
|
||||
element: Some(pallas::Base::from((1 << 6) - 1)),
|
||||
element: Value::known(pallas::Base::from((1 << 6) - 1)),
|
||||
num_bits: 6,
|
||||
};
|
||||
let prover = MockProver::<pallas::Base>::run(11, &circuit, vec![]).unwrap();
|
||||
@@ -592,7 +575,7 @@ mod tests {
|
||||
// Element larger than `num_bits` but within K bits
|
||||
{
|
||||
let circuit: MyCircuit<pallas::Base> = MyCircuit {
|
||||
element: Some(pallas::Base::from(1 << 6)),
|
||||
element: Value::known(pallas::Base::from(1 << 6)),
|
||||
num_bits: 6,
|
||||
};
|
||||
let prover = MockProver::<pallas::Base>::run(11, &circuit, vec![]).unwrap();
|
||||
@@ -611,7 +594,7 @@ mod tests {
|
||||
// Element larger than K bits
|
||||
{
|
||||
let circuit: MyCircuit<pallas::Base> = MyCircuit {
|
||||
element: Some(pallas::Base::from(1 << K)),
|
||||
element: Value::known(pallas::Base::from(1 << K)),
|
||||
num_bits: 6,
|
||||
};
|
||||
let prover = MockProver::<pallas::Base>::run(11, &circuit, vec![]).unwrap();
|
||||
@@ -648,7 +631,7 @@ mod tests {
|
||||
.invert()
|
||||
.unwrap();
|
||||
let circuit: MyCircuit<pallas::Base> = MyCircuit {
|
||||
element: Some(element),
|
||||
element: Value::known(element),
|
||||
num_bits: num_bits as usize,
|
||||
};
|
||||
let prover = MockProver::<pallas::Base>::run(11, &circuit, vec![]).unwrap();
|
||||
|
||||
@@ -6,6 +6,39 @@ and this project adheres to Rust's notion of
|
||||
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- `halo2_proofs::circuit::Value`, a more usable and type-safe replacement for
|
||||
`Option<V>` in circuit synthesis.
|
||||
- `impl Mul<F: Field> for &Assigned<F>`
|
||||
|
||||
### Changed
|
||||
All APIs that represented witnessed values as `Option<V>` now represent them as
|
||||
`halo2_proofs::circuit::Value<V>`. The core API changes are listed below.
|
||||
|
||||
- The following APIs now take `Value<_>` instead of `Option<_>`:
|
||||
- `halo2_proofs::plonk`:
|
||||
- `Assignment::fill_from_row`
|
||||
- The following APIs now take value closures that return `Value<V>` instead of
|
||||
`Result<V, Error>`:
|
||||
- `halo2_proofs::circuit`:
|
||||
- `Region::{assign_advice, assign_fixed}`
|
||||
- `Table::assign_cell`
|
||||
- `halo2_proofs::circuit::layouter`:
|
||||
- `RegionLayouter::{assign_advice, assign_fixed}`
|
||||
- `TableLayouter::assign_cell`
|
||||
- `halo2_proofs::plonk`:
|
||||
- `Assignment::{assign_advice, assign_fixed}`
|
||||
- The following APIs now return `Value<_>` instead of `Option<_>`:
|
||||
- `halo2_proofs::circuit`:
|
||||
- `AssignedCell::{value, value_field}`
|
||||
- The following APIs now return `Result<Value<F>, Error>` instead of
|
||||
`Result<Option<F>, Error>`:
|
||||
- `halo2_proofs::plonk`:
|
||||
- `Assignment::query_instance`
|
||||
- The following APIs now return `Result<(Cell, Value<F>), Error>` instead of
|
||||
`Result<(Cell, Option<F>), Error>`:
|
||||
- `halo2_proofs::circuit::layouter`:
|
||||
- `RegionLayouter::assign_advice_from_instance`
|
||||
|
||||
## [0.1.0] - 2022-05-10
|
||||
### Added
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
extern crate criterion;
|
||||
|
||||
use halo2_proofs::arithmetic::FieldExt;
|
||||
use halo2_proofs::circuit::{Layouter, SimpleFloorPlanner};
|
||||
use halo2_proofs::circuit::{Layouter, SimpleFloorPlanner, Value};
|
||||
use halo2_proofs::dev::MockProver;
|
||||
use halo2_proofs::plonk::*;
|
||||
use halo2_proofs::poly::Rotation;
|
||||
@@ -63,7 +63,7 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
|| format!("row {}", row),
|
||||
config.table,
|
||||
row as usize,
|
||||
|| Ok(F::from(row + 1)),
|
||||
|| Value::known(F::from(row + 1)),
|
||||
)?;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
|| format!("offset {}", offset),
|
||||
config.advice,
|
||||
offset as usize,
|
||||
|| Ok(F::from((offset % 256) + 1)),
|
||||
|| Value::known(F::from((offset % 256) + 1)),
|
||||
)?;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ extern crate criterion;
|
||||
|
||||
use group::ff::Field;
|
||||
use halo2_proofs::arithmetic::FieldExt;
|
||||
use halo2_proofs::circuit::{Cell, Layouter, SimpleFloorPlanner};
|
||||
use halo2_proofs::circuit::{Cell, Layouter, SimpleFloorPlanner, Value};
|
||||
use halo2_proofs::pasta::{EqAffine, Fp};
|
||||
use halo2_proofs::plonk::*;
|
||||
use halo2_proofs::poly::{commitment::Params, Rotation};
|
||||
@@ -38,20 +38,20 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
f: F,
|
||||
) -> Result<(Cell, Cell, Cell), Error>
|
||||
where
|
||||
F: FnMut() -> Result<(FF, FF, FF), Error>;
|
||||
F: FnMut() -> Value<(Assigned<FF>, Assigned<FF>, Assigned<FF>)>;
|
||||
fn raw_add<F>(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<FF>,
|
||||
f: F,
|
||||
) -> Result<(Cell, Cell, Cell), Error>
|
||||
where
|
||||
F: FnMut() -> Result<(FF, FF, FF), Error>;
|
||||
F: FnMut() -> Value<(Assigned<FF>, Assigned<FF>, Assigned<FF>)>;
|
||||
fn copy(&self, layouter: &mut impl Layouter<FF>, a: Cell, b: Cell) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct MyCircuit<F: FieldExt> {
|
||||
a: Option<F>,
|
||||
a: Value<F>,
|
||||
k: u32,
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
mut f: F,
|
||||
) -> Result<(Cell, Cell, Cell), Error>
|
||||
where
|
||||
F: FnMut() -> Result<(FF, FF, FF), Error>,
|
||||
F: FnMut() -> Value<(Assigned<FF>, Assigned<FF>, Assigned<FF>)>,
|
||||
{
|
||||
layouter.assign_region(
|
||||
|| "raw_multiply",
|
||||
@@ -87,27 +87,32 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
self.config.a,
|
||||
0,
|
||||
|| {
|
||||
value = Some(f()?);
|
||||
Ok(value.ok_or(Error::Synthesis)?.0)
|
||||
value = Some(f());
|
||||
value.unwrap().map(|v| v.0)
|
||||
},
|
||||
)?;
|
||||
let rhs = region.assign_advice(
|
||||
|| "rhs",
|
||||
self.config.b,
|
||||
0,
|
||||
|| Ok(value.ok_or(Error::Synthesis)?.1),
|
||||
|| value.unwrap().map(|v| v.1),
|
||||
)?;
|
||||
let out = region.assign_advice(
|
||||
|| "out",
|
||||
self.config.c,
|
||||
0,
|
||||
|| Ok(value.ok_or(Error::Synthesis)?.2),
|
||||
|| value.unwrap().map(|v| v.2),
|
||||
)?;
|
||||
|
||||
region.assign_fixed(|| "a", self.config.sa, 0, || Ok(FF::zero()))?;
|
||||
region.assign_fixed(|| "b", self.config.sb, 0, || Ok(FF::zero()))?;
|
||||
region.assign_fixed(|| "c", self.config.sc, 0, || Ok(FF::one()))?;
|
||||
region.assign_fixed(|| "a * b", self.config.sm, 0, || Ok(FF::one()))?;
|
||||
region.assign_fixed(|| "a", self.config.sa, 0, || Value::known(FF::zero()))?;
|
||||
region.assign_fixed(|| "b", self.config.sb, 0, || Value::known(FF::zero()))?;
|
||||
region.assign_fixed(|| "c", self.config.sc, 0, || Value::known(FF::one()))?;
|
||||
region.assign_fixed(
|
||||
|| "a * b",
|
||||
self.config.sm,
|
||||
0,
|
||||
|| Value::known(FF::one()),
|
||||
)?;
|
||||
Ok((lhs.cell(), rhs.cell(), out.cell()))
|
||||
},
|
||||
)
|
||||
@@ -118,7 +123,7 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
mut f: F,
|
||||
) -> Result<(Cell, Cell, Cell), Error>
|
||||
where
|
||||
F: FnMut() -> Result<(FF, FF, FF), Error>,
|
||||
F: FnMut() -> Value<(Assigned<FF>, Assigned<FF>, Assigned<FF>)>,
|
||||
{
|
||||
layouter.assign_region(
|
||||
|| "raw_add",
|
||||
@@ -129,27 +134,32 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
self.config.a,
|
||||
0,
|
||||
|| {
|
||||
value = Some(f()?);
|
||||
Ok(value.ok_or(Error::Synthesis)?.0)
|
||||
value = Some(f());
|
||||
value.unwrap().map(|v| v.0)
|
||||
},
|
||||
)?;
|
||||
let rhs = region.assign_advice(
|
||||
|| "rhs",
|
||||
self.config.b,
|
||||
0,
|
||||
|| Ok(value.ok_or(Error::Synthesis)?.1),
|
||||
|| value.unwrap().map(|v| v.1),
|
||||
)?;
|
||||
let out = region.assign_advice(
|
||||
|| "out",
|
||||
self.config.c,
|
||||
0,
|
||||
|| Ok(value.ok_or(Error::Synthesis)?.2),
|
||||
|| value.unwrap().map(|v| v.2),
|
||||
)?;
|
||||
|
||||
region.assign_fixed(|| "a", self.config.sa, 0, || Ok(FF::one()))?;
|
||||
region.assign_fixed(|| "b", self.config.sb, 0, || Ok(FF::one()))?;
|
||||
region.assign_fixed(|| "c", self.config.sc, 0, || Ok(FF::one()))?;
|
||||
region.assign_fixed(|| "a * b", self.config.sm, 0, || Ok(FF::zero()))?;
|
||||
region.assign_fixed(|| "a", self.config.sa, 0, || Value::known(FF::one()))?;
|
||||
region.assign_fixed(|| "b", self.config.sb, 0, || Value::known(FF::one()))?;
|
||||
region.assign_fixed(|| "c", self.config.sc, 0, || Value::known(FF::one()))?;
|
||||
region.assign_fixed(
|
||||
|| "a * b",
|
||||
self.config.sm,
|
||||
0,
|
||||
|| Value::known(FF::zero()),
|
||||
)?;
|
||||
Ok((lhs.cell(), rhs.cell(), out.cell()))
|
||||
},
|
||||
)
|
||||
@@ -169,7 +179,10 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
type FloorPlanner = SimpleFloorPlanner;
|
||||
|
||||
fn without_witnesses(&self) -> Self {
|
||||
Self { a: None, k: self.k }
|
||||
Self {
|
||||
a: Value::unknown(),
|
||||
k: self.k,
|
||||
}
|
||||
}
|
||||
|
||||
fn configure(meta: &mut ConstraintSystem<F>) -> PlonkConfig {
|
||||
@@ -220,22 +233,17 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
let cs = StandardPlonk::new(config);
|
||||
|
||||
for _ in 0..((1 << (self.k - 1)) - 3) {
|
||||
let mut a_squared = None;
|
||||
let a: Value<Assigned<_>> = self.a.into();
|
||||
let mut a_squared = Value::unknown();
|
||||
let (a0, _, c0) = cs.raw_multiply(&mut layouter, || {
|
||||
a_squared = self.a.map(|a| a.square());
|
||||
Ok((
|
||||
self.a.ok_or(Error::Synthesis)?,
|
||||
self.a.ok_or(Error::Synthesis)?,
|
||||
a_squared.ok_or(Error::Synthesis)?,
|
||||
))
|
||||
a_squared = a.square();
|
||||
a.zip(a_squared).map(|(a, a_squared)| (a, a, a_squared))
|
||||
})?;
|
||||
let (a1, b1, _) = cs.raw_add(&mut layouter, || {
|
||||
let fin = a_squared.and_then(|a2| self.a.map(|a| a + a2));
|
||||
Ok((
|
||||
self.a.ok_or(Error::Synthesis)?,
|
||||
a_squared.ok_or(Error::Synthesis)?,
|
||||
fin.ok_or(Error::Synthesis)?,
|
||||
))
|
||||
let fin = a_squared + a;
|
||||
a.zip(a_squared)
|
||||
.zip(fin)
|
||||
.map(|((a, a_squared), fin)| (a, a_squared, fin))
|
||||
})?;
|
||||
cs.copy(&mut layouter, a0, a1)?;
|
||||
cs.copy(&mut layouter, b1, c0)?;
|
||||
@@ -247,7 +255,10 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
|
||||
fn keygen(k: u32) -> (Params<EqAffine>, ProvingKey<EqAffine>) {
|
||||
let params: Params<EqAffine> = Params::new(k);
|
||||
let empty_circuit: MyCircuit<Fp> = MyCircuit { a: None, k };
|
||||
let empty_circuit: MyCircuit<Fp> = MyCircuit {
|
||||
a: Value::unknown(),
|
||||
k,
|
||||
};
|
||||
let vk = keygen_vk(¶ms, &empty_circuit).expect("keygen_vk should not fail");
|
||||
let pk = keygen_pk(¶ms, vk, &empty_circuit).expect("keygen_pk should not fail");
|
||||
(params, pk)
|
||||
@@ -257,7 +268,7 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
let rng = OsRng;
|
||||
|
||||
let circuit: MyCircuit<Fp> = MyCircuit {
|
||||
a: Some(Fp::random(rng)),
|
||||
a: Value::known(Fp::random(rng)),
|
||||
k,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use ff::Field;
|
||||
use halo2_proofs::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::{Cell, Layouter, Region, SimpleFloorPlanner},
|
||||
circuit::{Cell, Layouter, Region, SimpleFloorPlanner, Value},
|
||||
pasta::Fp,
|
||||
plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Fixed, TableColumn},
|
||||
plonk::{Advice, Assigned, Circuit, Column, ConstraintSystem, Error, Fixed, TableColumn},
|
||||
poly::Rotation,
|
||||
};
|
||||
use rand_core::OsRng;
|
||||
@@ -31,16 +31,16 @@ struct PlonkConfig {
|
||||
trait StandardCs<FF: FieldExt> {
|
||||
fn raw_multiply<F>(&self, region: &mut Region<FF>, f: F) -> Result<(Cell, Cell, Cell), Error>
|
||||
where
|
||||
F: FnMut() -> Result<(FF, FF, FF), Error>;
|
||||
F: FnMut() -> Value<(Assigned<FF>, Assigned<FF>, Assigned<FF>)>;
|
||||
fn raw_add<F>(&self, region: &mut Region<FF>, f: F) -> Result<(Cell, Cell, Cell), Error>
|
||||
where
|
||||
F: FnMut() -> Result<(FF, FF, FF), Error>;
|
||||
F: FnMut() -> Value<(Assigned<FF>, Assigned<FF>, Assigned<FF>)>;
|
||||
fn copy(&self, region: &mut Region<FF>, a: Cell, b: Cell) -> Result<(), Error>;
|
||||
fn lookup_table(&self, layouter: &mut impl Layouter<FF>, values: &[FF]) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
struct MyCircuit<F: FieldExt> {
|
||||
a: Option<F>,
|
||||
a: Value<F>,
|
||||
lookup_table: Vec<F>,
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ impl<FF: FieldExt> StandardCs<FF> for StandardPlonk<FF> {
|
||||
mut f: F,
|
||||
) -> Result<(Cell, Cell, Cell), Error>
|
||||
where
|
||||
F: FnMut() -> Result<(FF, FF, FF), Error>,
|
||||
F: FnMut() -> Value<(Assigned<FF>, Assigned<FF>, Assigned<FF>)>,
|
||||
{
|
||||
let mut value = None;
|
||||
let lhs = region.assign_advice(
|
||||
@@ -73,44 +73,36 @@ impl<FF: FieldExt> StandardCs<FF> for StandardPlonk<FF> {
|
||||
self.config.a,
|
||||
0,
|
||||
|| {
|
||||
value = Some(f()?);
|
||||
Ok(value.ok_or(Error::Synthesis)?.0)
|
||||
value = Some(f());
|
||||
value.unwrap().map(|v| v.0)
|
||||
},
|
||||
)?;
|
||||
region.assign_advice(
|
||||
|| "lhs^4",
|
||||
self.config.d,
|
||||
0,
|
||||
|| Ok(value.ok_or(Error::Synthesis)?.0.square().square()),
|
||||
)?;
|
||||
let rhs = region.assign_advice(
|
||||
|| "rhs",
|
||||
self.config.b,
|
||||
0,
|
||||
|| Ok(value.ok_or(Error::Synthesis)?.1),
|
||||
|| value.unwrap().map(|v| v.0).square().square(),
|
||||
)?;
|
||||
let rhs =
|
||||
region.assign_advice(|| "rhs", self.config.b, 0, || value.unwrap().map(|v| v.1))?;
|
||||
region.assign_advice(
|
||||
|| "rhs^4",
|
||||
self.config.e,
|
||||
0,
|
||||
|| Ok(value.ok_or(Error::Synthesis)?.1.square().square()),
|
||||
)?;
|
||||
let out = region.assign_advice(
|
||||
|| "out",
|
||||
self.config.c,
|
||||
0,
|
||||
|| Ok(value.ok_or(Error::Synthesis)?.2),
|
||||
|| value.unwrap().map(|v| v.1).square().square(),
|
||||
)?;
|
||||
let out =
|
||||
region.assign_advice(|| "out", self.config.c, 0, || value.unwrap().map(|v| v.2))?;
|
||||
|
||||
region.assign_fixed(|| "a", self.config.sa, 0, || Ok(FF::zero()))?;
|
||||
region.assign_fixed(|| "b", self.config.sb, 0, || Ok(FF::zero()))?;
|
||||
region.assign_fixed(|| "c", self.config.sc, 0, || Ok(FF::one()))?;
|
||||
region.assign_fixed(|| "a * b", self.config.sm, 0, || Ok(FF::one()))?;
|
||||
region.assign_fixed(|| "a", self.config.sa, 0, || Value::known(FF::zero()))?;
|
||||
region.assign_fixed(|| "b", self.config.sb, 0, || Value::known(FF::zero()))?;
|
||||
region.assign_fixed(|| "c", self.config.sc, 0, || Value::known(FF::one()))?;
|
||||
region.assign_fixed(|| "a * b", self.config.sm, 0, || Value::known(FF::one()))?;
|
||||
Ok((lhs.cell(), rhs.cell(), out.cell()))
|
||||
}
|
||||
fn raw_add<F>(&self, region: &mut Region<FF>, mut f: F) -> Result<(Cell, Cell, Cell), Error>
|
||||
where
|
||||
F: FnMut() -> Result<(FF, FF, FF), Error>,
|
||||
F: FnMut() -> Value<(Assigned<FF>, Assigned<FF>, Assigned<FF>)>,
|
||||
{
|
||||
let mut value = None;
|
||||
let lhs = region.assign_advice(
|
||||
@@ -118,39 +110,31 @@ impl<FF: FieldExt> StandardCs<FF> for StandardPlonk<FF> {
|
||||
self.config.a,
|
||||
0,
|
||||
|| {
|
||||
value = Some(f()?);
|
||||
Ok(value.ok_or(Error::Synthesis)?.0)
|
||||
value = Some(f());
|
||||
value.unwrap().map(|v| v.0)
|
||||
},
|
||||
)?;
|
||||
region.assign_advice(
|
||||
|| "lhs^4",
|
||||
self.config.d,
|
||||
0,
|
||||
|| Ok(value.ok_or(Error::Synthesis)?.0.square().square()),
|
||||
)?;
|
||||
let rhs = region.assign_advice(
|
||||
|| "rhs",
|
||||
self.config.b,
|
||||
0,
|
||||
|| Ok(value.ok_or(Error::Synthesis)?.1),
|
||||
|| value.unwrap().map(|v| v.0.square().square()),
|
||||
)?;
|
||||
let rhs =
|
||||
region.assign_advice(|| "rhs", self.config.b, 0, || value.unwrap().map(|v| v.1))?;
|
||||
region.assign_advice(
|
||||
|| "rhs^4",
|
||||
self.config.e,
|
||||
0,
|
||||
|| Ok(value.ok_or(Error::Synthesis)?.1.square().square()),
|
||||
)?;
|
||||
let out = region.assign_advice(
|
||||
|| "out",
|
||||
self.config.c,
|
||||
0,
|
||||
|| Ok(value.ok_or(Error::Synthesis)?.2),
|
||||
|| value.unwrap().map(|v| v.1.square().square()),
|
||||
)?;
|
||||
let out =
|
||||
region.assign_advice(|| "out", self.config.c, 0, || value.unwrap().map(|v| v.2))?;
|
||||
|
||||
region.assign_fixed(|| "a", self.config.sa, 0, || Ok(FF::one()))?;
|
||||
region.assign_fixed(|| "b", self.config.sb, 0, || Ok(FF::one()))?;
|
||||
region.assign_fixed(|| "c", self.config.sc, 0, || Ok(FF::one()))?;
|
||||
region.assign_fixed(|| "a * b", self.config.sm, 0, || Ok(FF::zero()))?;
|
||||
region.assign_fixed(|| "a", self.config.sa, 0, || Value::known(FF::one()))?;
|
||||
region.assign_fixed(|| "b", self.config.sb, 0, || Value::known(FF::one()))?;
|
||||
region.assign_fixed(|| "c", self.config.sc, 0, || Value::known(FF::one()))?;
|
||||
region.assign_fixed(|| "a * b", self.config.sm, 0, || Value::known(FF::zero()))?;
|
||||
Ok((lhs.cell(), rhs.cell(), out.cell()))
|
||||
}
|
||||
fn copy(&self, region: &mut Region<FF>, left: Cell, right: Cell) -> Result<(), Error> {
|
||||
@@ -161,7 +145,12 @@ impl<FF: FieldExt> StandardCs<FF> for StandardPlonk<FF> {
|
||||
|| "",
|
||||
|mut table| {
|
||||
for (index, &value) in values.iter().enumerate() {
|
||||
table.assign_cell(|| "table col", self.config.sl, index, || Ok(value))?;
|
||||
table.assign_cell(
|
||||
|| "table col",
|
||||
self.config.sl,
|
||||
index,
|
||||
|| Value::known(value),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
@@ -176,7 +165,7 @@ impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
|
||||
|
||||
fn without_witnesses(&self) -> Self {
|
||||
Self {
|
||||
a: None,
|
||||
a: Value::unknown(),
|
||||
lookup_table: self.lookup_table.clone(),
|
||||
}
|
||||
}
|
||||
@@ -257,22 +246,17 @@ impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
|
||||
layouter.assign_region(
|
||||
|| format!("region_{}", i),
|
||||
|mut region| {
|
||||
let mut a_squared = None;
|
||||
let a: Value<Assigned<_>> = self.a.into();
|
||||
let mut a_squared = Value::unknown();
|
||||
let (a0, _, c0) = cs.raw_multiply(&mut region, || {
|
||||
a_squared = self.a.map(|a| a.square());
|
||||
Ok((
|
||||
self.a.ok_or(Error::Synthesis)?,
|
||||
self.a.ok_or(Error::Synthesis)?,
|
||||
a_squared.ok_or(Error::Synthesis)?,
|
||||
))
|
||||
a_squared = a.square();
|
||||
a.zip(a_squared).map(|(a, a_squared)| (a, a, a_squared))
|
||||
})?;
|
||||
let (a1, b1, _) = cs.raw_add(&mut region, || {
|
||||
let fin = a_squared.and_then(|a2| self.a.map(|a| a + a2));
|
||||
Ok((
|
||||
self.a.ok_or(Error::Synthesis)?,
|
||||
a_squared.ok_or(Error::Synthesis)?,
|
||||
fin.ok_or(Error::Synthesis)?,
|
||||
))
|
||||
let fin = a_squared + a;
|
||||
a.zip(a_squared)
|
||||
.zip(fin)
|
||||
.map(|((a, a_squared), fin)| (a, a_squared, fin))
|
||||
})?;
|
||||
cs.copy(&mut region, a0, a1)?;
|
||||
cs.copy(&mut region, b1, c0)
|
||||
@@ -294,7 +278,7 @@ fn main() {
|
||||
let instance = Fp::one() + Fp::one();
|
||||
let lookup_table = vec![instance, a, a, Fp::zero()];
|
||||
let circuit: MyCircuit<Fp> = MyCircuit {
|
||||
a: None,
|
||||
a: Value::unknown(),
|
||||
lookup_table,
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::marker::PhantomData;
|
||||
|
||||
use halo2_proofs::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::{AssignedCell, Chip, Layouter, Region, SimpleFloorPlanner},
|
||||
circuit::{AssignedCell, Chip, Layouter, Region, SimpleFloorPlanner, Value},
|
||||
plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Fixed, Instance, Selector},
|
||||
poly::Rotation,
|
||||
};
|
||||
@@ -13,7 +13,7 @@ trait NumericInstructions<F: FieldExt>: Chip<F> {
|
||||
type Num;
|
||||
|
||||
/// Loads a number into the circuit as a private input.
|
||||
fn load_private(&self, layouter: impl Layouter<F>, a: Option<F>) -> Result<Self::Num, Error>;
|
||||
fn load_private(&self, layouter: impl Layouter<F>, a: Value<F>) -> Result<Self::Num, Error>;
|
||||
|
||||
/// Loads a number into the circuit as a fixed constant.
|
||||
fn load_constant(&self, layouter: impl Layouter<F>, constant: F) -> Result<Self::Num, Error>;
|
||||
@@ -151,7 +151,7 @@ impl<F: FieldExt> NumericInstructions<F> for FieldChip<F> {
|
||||
fn load_private(
|
||||
&self,
|
||||
mut layouter: impl Layouter<F>,
|
||||
value: Option<F>,
|
||||
value: Value<F>,
|
||||
) -> Result<Self::Num, Error> {
|
||||
let config = self.config();
|
||||
|
||||
@@ -159,12 +159,7 @@ impl<F: FieldExt> NumericInstructions<F> for FieldChip<F> {
|
||||
|| "load private",
|
||||
|mut region| {
|
||||
region
|
||||
.assign_advice(
|
||||
|| "private input",
|
||||
config.advice[0],
|
||||
0,
|
||||
|| value.ok_or(Error::Synthesis),
|
||||
)
|
||||
.assign_advice(|| "private input", config.advice[0], 0, || value)
|
||||
.map(Number)
|
||||
},
|
||||
)
|
||||
@@ -212,17 +207,12 @@ impl<F: FieldExt> NumericInstructions<F> for FieldChip<F> {
|
||||
|
||||
// Now we can assign the multiplication result, which is to be assigned
|
||||
// into the output position.
|
||||
let value = a.0.value().and_then(|a| b.0.value().map(|b| *a * *b));
|
||||
let value = a.0.value().copied() * b.0.value();
|
||||
|
||||
// Finally, we do the assignment to the output, returning a
|
||||
// variable to be used in another part of the circuit.
|
||||
region
|
||||
.assign_advice(
|
||||
|| "lhs * rhs",
|
||||
config.advice[0],
|
||||
1,
|
||||
|| value.ok_or(Error::Synthesis),
|
||||
)
|
||||
.assign_advice(|| "lhs * rhs", config.advice[0], 1, || value)
|
||||
.map(Number)
|
||||
},
|
||||
)
|
||||
@@ -250,8 +240,8 @@ impl<F: FieldExt> NumericInstructions<F> for FieldChip<F> {
|
||||
#[derive(Default)]
|
||||
struct MyCircuit<F: FieldExt> {
|
||||
constant: F,
|
||||
a: Option<F>,
|
||||
b: Option<F>,
|
||||
a: Value<F>,
|
||||
b: Value<F>,
|
||||
}
|
||||
|
||||
impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
|
||||
@@ -329,8 +319,8 @@ fn main() {
|
||||
// Instantiate the circuit with the private inputs.
|
||||
let circuit = MyCircuit {
|
||||
constant,
|
||||
a: Some(a),
|
||||
b: Some(b),
|
||||
a: Value::known(a),
|
||||
b: Value::known(b),
|
||||
};
|
||||
|
||||
// Arrange the public input. We expose the multiplication result in row 0
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::marker::PhantomData;
|
||||
|
||||
use halo2_proofs::{
|
||||
arithmetic::FieldExt,
|
||||
circuit::{AssignedCell, Chip, Layouter, Region, SimpleFloorPlanner},
|
||||
circuit::{AssignedCell, Chip, Layouter, Region, SimpleFloorPlanner, Value},
|
||||
plonk::{Advice, Circuit, Column, ConstraintSystem, Error, Instance, Selector},
|
||||
poly::Rotation,
|
||||
};
|
||||
@@ -20,7 +20,7 @@ trait FieldInstructions<F: FieldExt>: AddInstructions<F> + MulInstructions<F> {
|
||||
fn load_private(
|
||||
&self,
|
||||
layouter: impl Layouter<F>,
|
||||
a: Option<F>,
|
||||
a: Value<F>,
|
||||
) -> Result<<Self as FieldInstructions<F>>::Num, Error>;
|
||||
|
||||
/// Returns `d = (a + b) * c`.
|
||||
@@ -217,17 +217,12 @@ impl<F: FieldExt> AddInstructions<F> for AddChip<F> {
|
||||
|
||||
// Now we can compute the addition result, which is to be assigned
|
||||
// into the output position.
|
||||
let value = a.0.value().and_then(|a| b.0.value().map(|b| *a + *b));
|
||||
let value = a.0.value().copied() + b.0.value();
|
||||
|
||||
// Finally, we do the assignment to the output, returning a
|
||||
// variable to be used in another part of the circuit.
|
||||
region
|
||||
.assign_advice(
|
||||
|| "lhs + rhs",
|
||||
config.advice[0],
|
||||
1,
|
||||
|| value.ok_or(Error::Synthesis),
|
||||
)
|
||||
.assign_advice(|| "lhs + rhs", config.advice[0], 1, || value)
|
||||
.map(Number)
|
||||
},
|
||||
)
|
||||
@@ -343,17 +338,12 @@ impl<F: FieldExt> MulInstructions<F> for MulChip<F> {
|
||||
|
||||
// Now we can compute the multiplication result, which is to be assigned
|
||||
// into the output position.
|
||||
let value = a.0.value().and_then(|a| b.0.value().map(|b| *a * *b));
|
||||
let value = a.0.value().copied() * b.0.value();
|
||||
|
||||
// Finally, we do the assignment to the output, returning a
|
||||
// variable to be used in another part of the circuit.
|
||||
region
|
||||
.assign_advice(
|
||||
|| "lhs * rhs",
|
||||
config.advice[0],
|
||||
1,
|
||||
|| value.ok_or(Error::Synthesis),
|
||||
)
|
||||
.assign_advice(|| "lhs * rhs", config.advice[0], 1, || value)
|
||||
.map(Number)
|
||||
},
|
||||
)
|
||||
@@ -412,7 +402,7 @@ impl<F: FieldExt> FieldInstructions<F> for FieldChip<F> {
|
||||
fn load_private(
|
||||
&self,
|
||||
mut layouter: impl Layouter<F>,
|
||||
value: Option<F>,
|
||||
value: Value<F>,
|
||||
) -> Result<<Self as FieldInstructions<F>>::Num, Error> {
|
||||
let config = self.config();
|
||||
|
||||
@@ -420,12 +410,7 @@ impl<F: FieldExt> FieldInstructions<F> for FieldChip<F> {
|
||||
|| "load private",
|
||||
|mut region| {
|
||||
region
|
||||
.assign_advice(
|
||||
|| "private input",
|
||||
config.advice[0],
|
||||
0,
|
||||
|| value.ok_or(Error::Synthesis),
|
||||
)
|
||||
.assign_advice(|| "private input", config.advice[0], 0, || value)
|
||||
.map(Number)
|
||||
},
|
||||
)
|
||||
@@ -459,14 +444,14 @@ impl<F: FieldExt> FieldInstructions<F> for FieldChip<F> {
|
||||
// ANCHOR: circuit
|
||||
/// The full circuit implementation.
|
||||
///
|
||||
/// In this struct we store the private input variables. We use `Option<F>` because
|
||||
/// In this struct we store the private input variables. We use `Value<F>` because
|
||||
/// they won't have any value during key generation. During proving, if any of these
|
||||
/// were `None` we would get an error.
|
||||
/// were `Value::unknown()` we would get an error.
|
||||
#[derive(Default)]
|
||||
struct MyCircuit<F: FieldExt> {
|
||||
a: Option<F>,
|
||||
b: Option<F>,
|
||||
c: Option<F>,
|
||||
a: Value<F>,
|
||||
b: Value<F>,
|
||||
c: Value<F>,
|
||||
}
|
||||
|
||||
impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
|
||||
@@ -529,9 +514,9 @@ fn main() {
|
||||
|
||||
// Instantiate the circuit with the private inputs.
|
||||
let circuit = MyCircuit {
|
||||
a: Some(a),
|
||||
b: Some(b),
|
||||
c: Some(c),
|
||||
a: Value::known(a),
|
||||
b: Value::known(b),
|
||||
c: Value::known(c),
|
||||
};
|
||||
|
||||
// Arrange the public input. We expose the multiplication result in row 0
|
||||
|
||||
@@ -9,6 +9,9 @@ use crate::{
|
||||
plonk::{Advice, Any, Assigned, Column, Error, Fixed, Instance, Selector, TableColumn},
|
||||
};
|
||||
|
||||
mod value;
|
||||
pub use value::Value;
|
||||
|
||||
pub mod floor_planner;
|
||||
pub use floor_planner::single_pass::SimpleFloorPlanner;
|
||||
|
||||
@@ -95,14 +98,14 @@ pub struct Cell {
|
||||
/// An assigned cell.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AssignedCell<V, F: Field> {
|
||||
value: Option<V>,
|
||||
value: Value<V>,
|
||||
cell: Cell,
|
||||
_marker: PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<V, F: Field> AssignedCell<V, F> {
|
||||
/// Returns the value of the [`AssignedCell`].
|
||||
pub fn value(&self) -> Option<&V> {
|
||||
pub fn value(&self) -> Value<&V> {
|
||||
self.value.as_ref()
|
||||
}
|
||||
|
||||
@@ -117,8 +120,8 @@ where
|
||||
for<'v> Assigned<F>: From<&'v V>,
|
||||
{
|
||||
/// Returns the field element value of the [`AssignedCell`].
|
||||
pub fn value_field(&self) -> Option<Assigned<F>> {
|
||||
self.value().map(|v| v.into())
|
||||
pub fn value_field(&self) -> Value<Assigned<F>> {
|
||||
self.value.to_field()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +132,7 @@ impl<F: Field> AssignedCell<Assigned<F>, F> {
|
||||
/// If the denominator is zero, the returned cell's value is zero.
|
||||
pub fn evaluate(self) -> AssignedCell<F, F> {
|
||||
AssignedCell {
|
||||
value: self.value.map(|v| v.evaluate()),
|
||||
value: self.value.evaluate(),
|
||||
cell: self.cell,
|
||||
_marker: Default::default(),
|
||||
}
|
||||
@@ -155,9 +158,8 @@ where
|
||||
A: Fn() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
let assigned_cell = region.assign_advice(annotation, column, offset, || {
|
||||
self.value.clone().ok_or(Error::Synthesis)
|
||||
})?;
|
||||
let assigned_cell =
|
||||
region.assign_advice(annotation, column, offset, || self.value.clone())?;
|
||||
region.constrain_equal(assigned_cell.cell(), self.cell())?;
|
||||
|
||||
Ok(assigned_cell)
|
||||
@@ -213,19 +215,19 @@ impl<'r, F: Field> Region<'r, F> {
|
||||
mut to: V,
|
||||
) -> Result<AssignedCell<VR, F>, Error>
|
||||
where
|
||||
V: FnMut() -> Result<VR, Error> + 'v,
|
||||
V: FnMut() -> Value<VR> + 'v,
|
||||
for<'vr> Assigned<F>: From<&'vr VR>,
|
||||
A: Fn() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
let mut value = None;
|
||||
let mut value = Value::unknown();
|
||||
let cell =
|
||||
self.region
|
||||
.assign_advice(&|| annotation().into(), column, offset, &mut || {
|
||||
let v = to()?;
|
||||
let value_f = (&v).into();
|
||||
value = Some(v);
|
||||
Ok(value_f)
|
||||
let v = to();
|
||||
let value_f = v.to_field();
|
||||
value = v;
|
||||
value_f
|
||||
})?;
|
||||
|
||||
Ok(AssignedCell {
|
||||
@@ -261,7 +263,7 @@ impl<'r, F: Field> Region<'r, F> {
|
||||
)?;
|
||||
|
||||
Ok(AssignedCell {
|
||||
value: Some(constant),
|
||||
value: Value::known(constant),
|
||||
cell,
|
||||
_marker: PhantomData,
|
||||
})
|
||||
@@ -309,19 +311,19 @@ impl<'r, F: Field> Region<'r, F> {
|
||||
mut to: V,
|
||||
) -> Result<AssignedCell<VR, F>, Error>
|
||||
where
|
||||
V: FnMut() -> Result<VR, Error> + 'v,
|
||||
V: FnMut() -> Value<VR> + 'v,
|
||||
for<'vr> Assigned<F>: From<&'vr VR>,
|
||||
A: Fn() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
let mut value = None;
|
||||
let mut value = Value::unknown();
|
||||
let cell =
|
||||
self.region
|
||||
.assign_fixed(&|| annotation().into(), column, offset, &mut || {
|
||||
let v = to()?;
|
||||
let value_f = (&v).into();
|
||||
value = Some(v);
|
||||
Ok(value_f)
|
||||
let v = to();
|
||||
let value_f = v.to_field();
|
||||
value = v;
|
||||
value_f
|
||||
})?;
|
||||
|
||||
Ok(AssignedCell {
|
||||
@@ -376,14 +378,14 @@ impl<'r, F: Field> Table<'r, F> {
|
||||
mut to: V,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
V: FnMut() -> Result<VR, Error> + 'v,
|
||||
V: FnMut() -> Value<VR> + 'v,
|
||||
VR: Into<Assigned<F>>,
|
||||
A: Fn() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
self.table
|
||||
.assign_cell(&|| annotation().into(), column, offset, &mut || {
|
||||
to().map(|v| v.into())
|
||||
to().into_field()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use ff::Field;
|
||||
use crate::{
|
||||
circuit::{
|
||||
layouter::{RegionColumn, RegionLayouter, RegionShape, TableLayouter},
|
||||
Cell, Layouter, Region, RegionIndex, RegionStart, Table,
|
||||
Cell, Layouter, Region, RegionIndex, RegionStart, Table, Value,
|
||||
},
|
||||
plonk::{
|
||||
Advice, Any, Assigned, Assignment, Circuit, Column, Error, Fixed, FloorPlanner, Instance,
|
||||
@@ -131,7 +131,7 @@ impl<'a, F: Field, CS: Assignment<F> + 'a> Layouter<F> for SingleChipLayouter<'a
|
||||
|| format!("Constant({:?})", constant.evaluate()),
|
||||
constants_column,
|
||||
*next_constant_row,
|
||||
|| Ok(constant),
|
||||
|| Value::known(constant),
|
||||
)?;
|
||||
self.cs.copy(
|
||||
constants_column.into(),
|
||||
@@ -280,7 +280,7 @@ impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> RegionLayouter<F>
|
||||
annotation: &'v (dyn Fn() -> String + 'v),
|
||||
column: Column<Advice>,
|
||||
offset: usize,
|
||||
to: &'v mut (dyn FnMut() -> Result<Assigned<F>, Error> + 'v),
|
||||
to: &'v mut (dyn FnMut() -> Value<Assigned<F>> + 'v),
|
||||
) -> Result<Cell, Error> {
|
||||
self.layouter.cs.assign_advice(
|
||||
annotation,
|
||||
@@ -303,7 +303,8 @@ impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> RegionLayouter<F>
|
||||
offset: usize,
|
||||
constant: Assigned<F>,
|
||||
) -> Result<Cell, Error> {
|
||||
let advice = self.assign_advice(annotation, column, offset, &mut || Ok(constant))?;
|
||||
let advice =
|
||||
self.assign_advice(annotation, column, offset, &mut || Value::known(constant))?;
|
||||
self.constrain_constant(advice, constant)?;
|
||||
|
||||
Ok(advice)
|
||||
@@ -316,12 +317,10 @@ impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> RegionLayouter<F>
|
||||
row: usize,
|
||||
advice: Column<Advice>,
|
||||
offset: usize,
|
||||
) -> Result<(Cell, Option<F>), Error> {
|
||||
) -> Result<(Cell, Value<F>), Error> {
|
||||
let value = self.layouter.cs.query_instance(instance, row)?;
|
||||
|
||||
let cell = self.assign_advice(annotation, advice, offset, &mut || {
|
||||
value.ok_or(Error::Synthesis).map(|v| v.into())
|
||||
})?;
|
||||
let cell = self.assign_advice(annotation, advice, offset, &mut || value.to_field())?;
|
||||
|
||||
self.layouter.cs.copy(
|
||||
cell.column,
|
||||
@@ -338,7 +337,7 @@ impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> RegionLayouter<F>
|
||||
annotation: &'v (dyn Fn() -> String + 'v),
|
||||
column: Column<Fixed>,
|
||||
offset: usize,
|
||||
to: &'v mut (dyn FnMut() -> Result<Assigned<F>, Error> + 'v),
|
||||
to: &'v mut (dyn FnMut() -> Value<Assigned<F>> + 'v),
|
||||
) -> Result<Cell, Error> {
|
||||
self.layouter.cs.assign_fixed(
|
||||
annotation,
|
||||
@@ -376,9 +375,9 @@ impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> RegionLayouter<F>
|
||||
/// - The outer `Option` tracks whether the value in row 0 of the table column has been
|
||||
/// assigned yet. This will always be `Some` once a valid table has been completely
|
||||
/// assigned.
|
||||
/// - The inner `Option` tracks whether the underlying `Assignment` is evaluating
|
||||
/// - The inner `Value` tracks whether the underlying `Assignment` is evaluating
|
||||
/// witnesses or not.
|
||||
type DefaultTableValue<F> = Option<Option<Assigned<F>>>;
|
||||
type DefaultTableValue<F> = Option<Value<Assigned<F>>>;
|
||||
|
||||
pub(crate) struct SimpleTableLayouter<'r, 'a, F: Field, CS: Assignment<F> + 'a> {
|
||||
cs: &'a mut CS,
|
||||
@@ -414,7 +413,7 @@ impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> TableLayouter<F>
|
||||
annotation: &'v (dyn Fn() -> String + 'v),
|
||||
column: TableColumn,
|
||||
offset: usize,
|
||||
to: &'v mut (dyn FnMut() -> Result<Assigned<F>, Error> + 'v),
|
||||
to: &'v mut (dyn FnMut() -> Value<Assigned<F>> + 'v),
|
||||
) -> Result<(), Error> {
|
||||
if self.used_columns.contains(&column) {
|
||||
return Err(Error::Synthesis); // TODO better error
|
||||
@@ -422,14 +421,14 @@ impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> TableLayouter<F>
|
||||
|
||||
let entry = self.default_and_assigned.entry(column).or_default();
|
||||
|
||||
let mut value = None;
|
||||
let mut value = Value::unknown();
|
||||
self.cs.assign_fixed(
|
||||
annotation,
|
||||
column.inner(),
|
||||
offset, // tables are always assigned starting at row 0
|
||||
|| {
|
||||
let res = to();
|
||||
value = res.as_ref().ok().cloned();
|
||||
value = res;
|
||||
res
|
||||
},
|
||||
)?;
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{
|
||||
circuit::{
|
||||
floor_planner::single_pass::SimpleTableLayouter,
|
||||
layouter::{RegionColumn, RegionLayouter, RegionShape, TableLayouter},
|
||||
Cell, Layouter, Region, RegionIndex, RegionStart, Table,
|
||||
Cell, Layouter, Region, RegionIndex, RegionStart, Table, Value,
|
||||
},
|
||||
plonk::{
|
||||
Advice, Any, Assigned, Assignment, Circuit, Column, Error, Fixed, FloorPlanner, Instance,
|
||||
@@ -126,7 +126,7 @@ impl FloorPlanner for V1 {
|
||||
|| format!("Constant({:?})", value.evaluate()),
|
||||
fixed_column,
|
||||
fixed_row,
|
||||
|| Ok(value),
|
||||
|| Value::known(value),
|
||||
)?;
|
||||
plan.cs.copy(
|
||||
fixed_column.into(),
|
||||
@@ -396,7 +396,7 @@ impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> RegionLayouter<F> for V1Region<'r
|
||||
annotation: &'v (dyn Fn() -> String + 'v),
|
||||
column: Column<Advice>,
|
||||
offset: usize,
|
||||
to: &'v mut (dyn FnMut() -> Result<Assigned<F>, Error> + 'v),
|
||||
to: &'v mut (dyn FnMut() -> Value<Assigned<F>> + 'v),
|
||||
) -> Result<Cell, Error> {
|
||||
self.plan.cs.assign_advice(
|
||||
annotation,
|
||||
@@ -419,7 +419,8 @@ impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> RegionLayouter<F> for V1Region<'r
|
||||
offset: usize,
|
||||
constant: Assigned<F>,
|
||||
) -> Result<Cell, Error> {
|
||||
let advice = self.assign_advice(annotation, column, offset, &mut || Ok(constant))?;
|
||||
let advice =
|
||||
self.assign_advice(annotation, column, offset, &mut || Value::known(constant))?;
|
||||
self.constrain_constant(advice, constant)?;
|
||||
|
||||
Ok(advice)
|
||||
@@ -432,12 +433,10 @@ impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> RegionLayouter<F> for V1Region<'r
|
||||
row: usize,
|
||||
advice: Column<Advice>,
|
||||
offset: usize,
|
||||
) -> Result<(Cell, Option<F>), Error> {
|
||||
) -> Result<(Cell, Value<F>), Error> {
|
||||
let value = self.plan.cs.query_instance(instance, row)?;
|
||||
|
||||
let cell = self.assign_advice(annotation, advice, offset, &mut || {
|
||||
value.ok_or(Error::Synthesis).map(|v| v.into())
|
||||
})?;
|
||||
let cell = self.assign_advice(annotation, advice, offset, &mut || value.to_field())?;
|
||||
|
||||
self.plan.cs.copy(
|
||||
cell.column,
|
||||
@@ -454,7 +453,7 @@ impl<'r, 'a, F: Field, CS: Assignment<F> + 'a> RegionLayouter<F> for V1Region<'r
|
||||
annotation: &'v (dyn Fn() -> String + 'v),
|
||||
column: Column<Fixed>,
|
||||
offset: usize,
|
||||
to: &'v mut (dyn FnMut() -> Result<Assigned<F>, Error> + 'v),
|
||||
to: &'v mut (dyn FnMut() -> Value<Assigned<F>> + 'v),
|
||||
) -> Result<Cell, Error> {
|
||||
self.plan.cs.assign_fixed(
|
||||
annotation,
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::fmt;
|
||||
|
||||
use ff::Field;
|
||||
|
||||
use super::{Cell, RegionIndex};
|
||||
use super::{Cell, RegionIndex, Value};
|
||||
use crate::plonk::{Advice, Any, Assigned, Column, Error, Fixed, Instance, Selector, TableColumn};
|
||||
|
||||
/// Helper trait for implementing a custom [`Layouter`].
|
||||
@@ -54,7 +54,7 @@ pub trait RegionLayouter<F: Field>: fmt::Debug {
|
||||
annotation: &'v (dyn Fn() -> String + 'v),
|
||||
column: Column<Advice>,
|
||||
offset: usize,
|
||||
to: &'v mut (dyn FnMut() -> Result<Assigned<F>, Error> + 'v),
|
||||
to: &'v mut (dyn FnMut() -> Value<Assigned<F>> + 'v),
|
||||
) -> Result<Cell, Error>;
|
||||
|
||||
/// Assigns a constant value to the column `advice` at `offset` within this region.
|
||||
@@ -82,7 +82,7 @@ pub trait RegionLayouter<F: Field>: fmt::Debug {
|
||||
row: usize,
|
||||
advice: Column<Advice>,
|
||||
offset: usize,
|
||||
) -> Result<(Cell, Option<F>), Error>;
|
||||
) -> Result<(Cell, Value<F>), Error>;
|
||||
|
||||
/// Assign a fixed value
|
||||
fn assign_fixed<'v>(
|
||||
@@ -90,7 +90,7 @@ pub trait RegionLayouter<F: Field>: fmt::Debug {
|
||||
annotation: &'v (dyn Fn() -> String + 'v),
|
||||
column: Column<Fixed>,
|
||||
offset: usize,
|
||||
to: &'v mut (dyn FnMut() -> Result<Assigned<F>, Error> + 'v),
|
||||
to: &'v mut (dyn FnMut() -> Value<Assigned<F>> + 'v),
|
||||
) -> Result<Cell, Error>;
|
||||
|
||||
/// Constrains a cell to have a constant value.
|
||||
@@ -118,7 +118,7 @@ pub trait TableLayouter<F: Field>: fmt::Debug {
|
||||
annotation: &'v (dyn Fn() -> String + 'v),
|
||||
column: TableColumn,
|
||||
offset: usize,
|
||||
to: &'v mut (dyn FnMut() -> Result<Assigned<F>, Error> + 'v),
|
||||
to: &'v mut (dyn FnMut() -> Value<Assigned<F>> + 'v),
|
||||
) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ impl<F: Field> RegionLayouter<F> for RegionShape {
|
||||
_: &'v (dyn Fn() -> String + 'v),
|
||||
column: Column<Advice>,
|
||||
offset: usize,
|
||||
_to: &'v mut (dyn FnMut() -> Result<Assigned<F>, Error> + 'v),
|
||||
_to: &'v mut (dyn FnMut() -> Value<Assigned<F>> + 'v),
|
||||
) -> Result<Cell, Error> {
|
||||
self.columns.insert(Column::<Any>::from(column).into());
|
||||
self.row_count = cmp::max(self.row_count, offset + 1);
|
||||
@@ -234,7 +234,7 @@ impl<F: Field> RegionLayouter<F> for RegionShape {
|
||||
constant: Assigned<F>,
|
||||
) -> Result<Cell, Error> {
|
||||
// The rest is identical to witnessing an advice cell.
|
||||
self.assign_advice(annotation, column, offset, &mut || Ok(constant))
|
||||
self.assign_advice(annotation, column, offset, &mut || Value::known(constant))
|
||||
}
|
||||
|
||||
fn assign_advice_from_instance<'v>(
|
||||
@@ -244,7 +244,7 @@ impl<F: Field> RegionLayouter<F> for RegionShape {
|
||||
_: usize,
|
||||
advice: Column<Advice>,
|
||||
offset: usize,
|
||||
) -> Result<(Cell, Option<F>), Error> {
|
||||
) -> Result<(Cell, Value<F>), Error> {
|
||||
self.columns.insert(Column::<Any>::from(advice).into());
|
||||
self.row_count = cmp::max(self.row_count, offset + 1);
|
||||
|
||||
@@ -254,7 +254,7 @@ impl<F: Field> RegionLayouter<F> for RegionShape {
|
||||
row_offset: offset,
|
||||
column: advice.into(),
|
||||
},
|
||||
None,
|
||||
Value::unknown(),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ impl<F: Field> RegionLayouter<F> for RegionShape {
|
||||
_: &'v (dyn Fn() -> String + 'v),
|
||||
column: Column<Fixed>,
|
||||
offset: usize,
|
||||
_to: &'v mut (dyn FnMut() -> Result<Assigned<F>, Error> + 'v),
|
||||
_to: &'v mut (dyn FnMut() -> Value<Assigned<F>> + 'v),
|
||||
) -> Result<Cell, Error> {
|
||||
self.columns.insert(Column::<Any>::from(column).into());
|
||||
self.row_count = cmp::max(self.row_count, offset + 1);
|
||||
|
||||
698
halo2_proofs/src/circuit/value.rs
Normal file
698
halo2_proofs/src/circuit/value.rs
Normal file
@@ -0,0 +1,698 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::ops::{Add, Mul, Neg, Sub};
|
||||
|
||||
use group::ff::Field;
|
||||
|
||||
use crate::plonk::{Assigned, Error};
|
||||
|
||||
/// A value that might exist within a circuit.
|
||||
///
|
||||
/// This behaves like `Option<V>` but differs in two key ways:
|
||||
/// - It does not expose the enum cases, or provide an `Option::unwrap` equivalent. This
|
||||
/// helps to ensure that unwitnessed values correctly propagate.
|
||||
/// - It provides pass-through implementations of common traits such as `Add` and `Mul`,
|
||||
/// for improved usability.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Value<V> {
|
||||
inner: Option<V>,
|
||||
}
|
||||
|
||||
impl<V> Default for Value<V> {
|
||||
fn default() -> Self {
|
||||
Self::unknown()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Value<V> {
|
||||
/// Constructs an unwitnessed value.
|
||||
pub const fn unknown() -> Self {
|
||||
Self { inner: None }
|
||||
}
|
||||
|
||||
/// Constructs a known value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use halo2_proofs::circuit::Value;
|
||||
///
|
||||
/// let v = Value::known(37);
|
||||
/// ```
|
||||
pub const fn known(value: V) -> Self {
|
||||
Self { inner: Some(value) }
|
||||
}
|
||||
|
||||
/// Obtains the inner value for assigning into the circuit.
|
||||
///
|
||||
/// Returns `Error::Synthesis` if this is [`Value::unknown()`].
|
||||
pub(crate) fn assign(self) -> Result<V, Error> {
|
||||
self.inner.ok_or(Error::Synthesis)
|
||||
}
|
||||
|
||||
/// Converts from `&Value<V>` to `Value<&V>`.
|
||||
pub fn as_ref(&self) -> Value<&V> {
|
||||
Value {
|
||||
inner: self.inner.as_ref(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts from `&mut Value<V>` to `Value<&mut V>`.
|
||||
pub fn as_mut(&mut self) -> Value<&mut V> {
|
||||
Value {
|
||||
inner: self.inner.as_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Enforces an assertion on the contained value, if known.
|
||||
///
|
||||
/// The assertion is ignored if `self` is [`Value::unknown()`]. Do not try to enforce
|
||||
/// circuit constraints with this method!
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `f` returns `false`.
|
||||
pub fn assert_if_known<F: FnOnce(&V) -> bool>(&self, f: F) {
|
||||
if let Some(value) = self.inner.as_ref() {
|
||||
assert!(f(value));
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks the contained value for an error condition, if known.
|
||||
///
|
||||
/// The error check is ignored if `self` is [`Value::unknown()`]. Do not try to
|
||||
/// enforce circuit constraints with this method!
|
||||
pub fn error_if_known_and<F: FnOnce(&V) -> bool>(&self, f: F) -> Result<(), Error> {
|
||||
match self.inner.as_ref() {
|
||||
Some(value) if f(value) => Err(Error::Synthesis),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a `Value<V>` to `Value<W>` by applying a function to the contained value.
|
||||
pub fn map<W, F: FnOnce(V) -> W>(self, f: F) -> Value<W> {
|
||||
Value {
|
||||
inner: self.inner.map(f),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns [`Value::unknown()`] if the value is [`Value::unknown()`], otherwise calls
|
||||
/// `f` with the wrapped value and returns the result.
|
||||
pub fn and_then<W, F: FnOnce(V) -> Value<W>>(self, f: F) -> Value<W> {
|
||||
match self.inner {
|
||||
Some(v) => f(v),
|
||||
None => Value::unknown(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Zips `self` with another `Value`.
|
||||
///
|
||||
/// If `self` is `Value::known(s)` and `other` is `Value::known(o)`, this method
|
||||
/// returns `Value::known((s, o))`. Otherwise, [`Value::unknown()`] is returned.
|
||||
pub fn zip<W>(self, other: Value<W>) -> Value<(V, W)> {
|
||||
Value {
|
||||
inner: self.inner.zip(other.inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, W> Value<(V, W)> {
|
||||
/// Unzips a value containing a tuple of two values.
|
||||
///
|
||||
/// If `self` is `Value::known((a, b)), this method returns
|
||||
/// `(Value::known(a), Value::known(b))`. Otherwise,
|
||||
/// `(Value::unknown(), Value::unknown())` is returned.
|
||||
pub fn unzip(self) -> (Value<V>, Value<W>) {
|
||||
match self.inner {
|
||||
Some((a, b)) => (Value::known(a), Value::known(b)),
|
||||
None => (Value::unknown(), Value::unknown()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Value<&V> {
|
||||
/// Maps a `Value<&V>` to a `Value<V>` by copying the contents of the value.
|
||||
#[must_use = "`self` will be dropped if the result is not used"]
|
||||
pub fn copied(self) -> Value<V>
|
||||
where
|
||||
V: Copy,
|
||||
{
|
||||
Value {
|
||||
inner: self.inner.copied(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a `Value<&V>` to a `Value<V>` by cloning the contents of the value.
|
||||
#[must_use = "`self` will be dropped if the result is not used"]
|
||||
pub fn cloned(self) -> Value<V>
|
||||
where
|
||||
V: Clone,
|
||||
{
|
||||
Value {
|
||||
inner: self.inner.cloned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Value<&mut V> {
|
||||
/// Maps a `Value<&mut V>` to a `Value<V>` by copying the contents of the value.
|
||||
#[must_use = "`self` will be dropped if the result is not used"]
|
||||
pub fn copied(self) -> Value<V>
|
||||
where
|
||||
V: Copy,
|
||||
{
|
||||
Value {
|
||||
inner: self.inner.copied(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Maps a `Value<&mut V>` to a `Value<V>` by cloning the contents of the value.
|
||||
#[must_use = "`self` will be dropped if the result is not used"]
|
||||
pub fn cloned(self) -> Value<V>
|
||||
where
|
||||
V: Clone,
|
||||
{
|
||||
Value {
|
||||
inner: self.inner.cloned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Copy, const LEN: usize> Value<[V; LEN]> {
|
||||
/// Transposes a `Value<[V; LEN]>` into a `[Value<V>; LEN]`.
|
||||
///
|
||||
/// [`Value::unknown()`] will be mapped to `[Value::unknown(); LEN]`.
|
||||
pub fn transpose_array(self) -> [Value<V>; LEN] {
|
||||
let mut ret = [Value::unknown(); LEN];
|
||||
if let Some(arr) = self.inner {
|
||||
for (entry, value) in ret.iter_mut().zip(arr) {
|
||||
*entry = Value::known(value);
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, I> Value<I>
|
||||
where
|
||||
I: IntoIterator<Item = V>,
|
||||
I::IntoIter: ExactSizeIterator,
|
||||
{
|
||||
/// Transposes a `Value<impl IntoIterator<Item = V>>` into a `Vec<Value<V>>`.
|
||||
///
|
||||
/// [`Value::unknown()`] will be mapped to `vec![Value::unknown(); length]`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `self` is `Value::known(values)` and `values.len() != length`.
|
||||
pub fn transpose_vec(self, length: usize) -> Vec<Value<V>> {
|
||||
match self.inner {
|
||||
Some(values) => {
|
||||
let values = values.into_iter();
|
||||
assert_eq!(values.len(), length);
|
||||
values.map(Value::known).collect()
|
||||
}
|
||||
None => (0..length).map(|_| Value::unknown()).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// FromIterator
|
||||
//
|
||||
|
||||
impl<A, V: FromIterator<A>> FromIterator<Value<A>> for Value<V> {
|
||||
/// Takes each element in the [`Iterator`]: if it is [`Value::unknown()`], no further
|
||||
/// elements are taken, and the [`Value::unknown()`] is returned. Should no
|
||||
/// [`Value::unknown()`] occur, a container of type `V` containing the values of each
|
||||
/// [`Value`] is returned.
|
||||
fn from_iter<I: IntoIterator<Item = Value<A>>>(iter: I) -> Self {
|
||||
Self {
|
||||
inner: iter.into_iter().map(|v| v.inner).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Neg
|
||||
//
|
||||
|
||||
impl<V: Neg> Neg for Value<V> {
|
||||
type Output = Value<V::Output>;
|
||||
|
||||
fn neg(self) -> Self::Output {
|
||||
Value {
|
||||
inner: self.inner.map(|v| -v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Add
|
||||
//
|
||||
|
||||
impl<V, O> Add for Value<V>
|
||||
where
|
||||
V: Add<Output = O>,
|
||||
{
|
||||
type Output = Value<O>;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Value {
|
||||
inner: self.inner.zip(rhs.inner).map(|(a, b)| a + b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, O> Add for &Value<V>
|
||||
where
|
||||
for<'v> &'v V: Add<Output = O>,
|
||||
{
|
||||
type Output = Value<O>;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
Value {
|
||||
inner: self
|
||||
.inner
|
||||
.as_ref()
|
||||
.zip(rhs.inner.as_ref())
|
||||
.map(|(a, b)| a + b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, O> Add<Value<&V>> for Value<V>
|
||||
where
|
||||
for<'v> V: Add<&'v V, Output = O>,
|
||||
{
|
||||
type Output = Value<O>;
|
||||
|
||||
fn add(self, rhs: Value<&V>) -> Self::Output {
|
||||
Value {
|
||||
inner: self.inner.zip(rhs.inner).map(|(a, b)| a + b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, O> Add<Value<V>> for Value<&V>
|
||||
where
|
||||
for<'v> &'v V: Add<V, Output = O>,
|
||||
{
|
||||
type Output = Value<O>;
|
||||
|
||||
fn add(self, rhs: Value<V>) -> Self::Output {
|
||||
Value {
|
||||
inner: self.inner.zip(rhs.inner).map(|(a, b)| a + b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, O> Add<&Value<V>> for Value<V>
|
||||
where
|
||||
for<'v> V: Add<&'v V, Output = O>,
|
||||
{
|
||||
type Output = Value<O>;
|
||||
|
||||
fn add(self, rhs: &Self) -> Self::Output {
|
||||
self + rhs.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, O> Add<Value<V>> for &Value<V>
|
||||
where
|
||||
for<'v> &'v V: Add<V, Output = O>,
|
||||
{
|
||||
type Output = Value<O>;
|
||||
|
||||
fn add(self, rhs: Value<V>) -> Self::Output {
|
||||
self.as_ref() + rhs
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Sub
|
||||
//
|
||||
|
||||
impl<V, O> Sub for Value<V>
|
||||
where
|
||||
V: Sub<Output = O>,
|
||||
{
|
||||
type Output = Value<O>;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
Value {
|
||||
inner: self.inner.zip(rhs.inner).map(|(a, b)| a - b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, O> Sub for &Value<V>
|
||||
where
|
||||
for<'v> &'v V: Sub<Output = O>,
|
||||
{
|
||||
type Output = Value<O>;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
Value {
|
||||
inner: self
|
||||
.inner
|
||||
.as_ref()
|
||||
.zip(rhs.inner.as_ref())
|
||||
.map(|(a, b)| a - b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, O> Sub<Value<&V>> for Value<V>
|
||||
where
|
||||
for<'v> V: Sub<&'v V, Output = O>,
|
||||
{
|
||||
type Output = Value<O>;
|
||||
|
||||
fn sub(self, rhs: Value<&V>) -> Self::Output {
|
||||
Value {
|
||||
inner: self.inner.zip(rhs.inner).map(|(a, b)| a - b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, O> Sub<Value<V>> for Value<&V>
|
||||
where
|
||||
for<'v> &'v V: Sub<V, Output = O>,
|
||||
{
|
||||
type Output = Value<O>;
|
||||
|
||||
fn sub(self, rhs: Value<V>) -> Self::Output {
|
||||
Value {
|
||||
inner: self.inner.zip(rhs.inner).map(|(a, b)| a - b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, O> Sub<&Value<V>> for Value<V>
|
||||
where
|
||||
for<'v> V: Sub<&'v V, Output = O>,
|
||||
{
|
||||
type Output = Value<O>;
|
||||
|
||||
fn sub(self, rhs: &Self) -> Self::Output {
|
||||
self - rhs.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, O> Sub<Value<V>> for &Value<V>
|
||||
where
|
||||
for<'v> &'v V: Sub<V, Output = O>,
|
||||
{
|
||||
type Output = Value<O>;
|
||||
|
||||
fn sub(self, rhs: Value<V>) -> Self::Output {
|
||||
self.as_ref() - rhs
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Mul
|
||||
//
|
||||
|
||||
impl<V, O> Mul for Value<V>
|
||||
where
|
||||
V: Mul<Output = O>,
|
||||
{
|
||||
type Output = Value<O>;
|
||||
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
Value {
|
||||
inner: self.inner.zip(rhs.inner).map(|(a, b)| a * b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, O> Mul for &Value<V>
|
||||
where
|
||||
for<'v> &'v V: Mul<Output = O>,
|
||||
{
|
||||
type Output = Value<O>;
|
||||
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
Value {
|
||||
inner: self
|
||||
.inner
|
||||
.as_ref()
|
||||
.zip(rhs.inner.as_ref())
|
||||
.map(|(a, b)| a * b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, O> Mul<Value<&V>> for Value<V>
|
||||
where
|
||||
for<'v> V: Mul<&'v V, Output = O>,
|
||||
{
|
||||
type Output = Value<O>;
|
||||
|
||||
fn mul(self, rhs: Value<&V>) -> Self::Output {
|
||||
Value {
|
||||
inner: self.inner.zip(rhs.inner).map(|(a, b)| a * b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, O> Mul<Value<V>> for Value<&V>
|
||||
where
|
||||
for<'v> &'v V: Mul<V, Output = O>,
|
||||
{
|
||||
type Output = Value<O>;
|
||||
|
||||
fn mul(self, rhs: Value<V>) -> Self::Output {
|
||||
Value {
|
||||
inner: self.inner.zip(rhs.inner).map(|(a, b)| a * b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, O> Mul<&Value<V>> for Value<V>
|
||||
where
|
||||
for<'v> V: Mul<&'v V, Output = O>,
|
||||
{
|
||||
type Output = Value<O>;
|
||||
|
||||
fn mul(self, rhs: &Self) -> Self::Output {
|
||||
self * rhs.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, O> Mul<Value<V>> for &Value<V>
|
||||
where
|
||||
for<'v> &'v V: Mul<V, Output = O>,
|
||||
{
|
||||
type Output = Value<O>;
|
||||
|
||||
fn mul(self, rhs: Value<V>) -> Self::Output {
|
||||
self.as_ref() * rhs
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Assigned<Field>
|
||||
//
|
||||
|
||||
impl<F: Field> From<Value<F>> for Value<Assigned<F>> {
|
||||
fn from(value: Value<F>) -> Self {
|
||||
Self {
|
||||
inner: value.inner.map(Assigned::from),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Add<Value<F>> for Value<Assigned<F>> {
|
||||
type Output = Value<Assigned<F>>;
|
||||
|
||||
fn add(self, rhs: Value<F>) -> Self::Output {
|
||||
Value {
|
||||
inner: self.inner.zip(rhs.inner).map(|(a, b)| a + b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Add<F> for Value<Assigned<F>> {
|
||||
type Output = Value<Assigned<F>>;
|
||||
|
||||
fn add(self, rhs: F) -> Self::Output {
|
||||
self + Value::known(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Add<Value<F>> for Value<&Assigned<F>> {
|
||||
type Output = Value<Assigned<F>>;
|
||||
|
||||
fn add(self, rhs: Value<F>) -> Self::Output {
|
||||
Value {
|
||||
inner: self.inner.zip(rhs.inner).map(|(a, b)| a + b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Add<F> for Value<&Assigned<F>> {
|
||||
type Output = Value<Assigned<F>>;
|
||||
|
||||
fn add(self, rhs: F) -> Self::Output {
|
||||
self + Value::known(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Sub<Value<F>> for Value<Assigned<F>> {
|
||||
type Output = Value<Assigned<F>>;
|
||||
|
||||
fn sub(self, rhs: Value<F>) -> Self::Output {
|
||||
Value {
|
||||
inner: self.inner.zip(rhs.inner).map(|(a, b)| a - b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Sub<F> for Value<Assigned<F>> {
|
||||
type Output = Value<Assigned<F>>;
|
||||
|
||||
fn sub(self, rhs: F) -> Self::Output {
|
||||
self - Value::known(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Sub<Value<F>> for Value<&Assigned<F>> {
|
||||
type Output = Value<Assigned<F>>;
|
||||
|
||||
fn sub(self, rhs: Value<F>) -> Self::Output {
|
||||
Value {
|
||||
inner: self.inner.zip(rhs.inner).map(|(a, b)| a - b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Sub<F> for Value<&Assigned<F>> {
|
||||
type Output = Value<Assigned<F>>;
|
||||
|
||||
fn sub(self, rhs: F) -> Self::Output {
|
||||
self - Value::known(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Mul<Value<F>> for Value<Assigned<F>> {
|
||||
type Output = Value<Assigned<F>>;
|
||||
|
||||
fn mul(self, rhs: Value<F>) -> Self::Output {
|
||||
Value {
|
||||
inner: self.inner.zip(rhs.inner).map(|(a, b)| a * b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Mul<F> for Value<Assigned<F>> {
|
||||
type Output = Value<Assigned<F>>;
|
||||
|
||||
fn mul(self, rhs: F) -> Self::Output {
|
||||
self * Value::known(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Mul<Value<F>> for Value<&Assigned<F>> {
|
||||
type Output = Value<Assigned<F>>;
|
||||
|
||||
fn mul(self, rhs: Value<F>) -> Self::Output {
|
||||
Value {
|
||||
inner: self.inner.zip(rhs.inner).map(|(a, b)| a * b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Mul<F> for Value<&Assigned<F>> {
|
||||
type Output = Value<Assigned<F>>;
|
||||
|
||||
fn mul(self, rhs: F) -> Self::Output {
|
||||
self * Value::known(rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Value<V> {
|
||||
/// Returns the field element corresponding to this value.
|
||||
pub fn to_field<F: Field>(&self) -> Value<Assigned<F>>
|
||||
where
|
||||
for<'v> Assigned<F>: From<&'v V>,
|
||||
{
|
||||
Value {
|
||||
inner: self.inner.as_ref().map(|v| v.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the field element corresponding to this value.
|
||||
pub fn into_field<F: Field>(self) -> Value<Assigned<F>>
|
||||
where
|
||||
V: Into<Assigned<F>>,
|
||||
{
|
||||
Value {
|
||||
inner: self.inner.map(|v| v.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Doubles this field element.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// If you have a `Value<F: Field>`, convert it to `Value<Assigned<F>>` first:
|
||||
/// ```
|
||||
/// # use pasta_curves::pallas::Base as F;
|
||||
/// use halo2_proofs::{circuit::Value, plonk::Assigned};
|
||||
///
|
||||
/// let v = Value::known(F::from(2));
|
||||
/// let v: Value<Assigned<F>> = v.into();
|
||||
/// v.double();
|
||||
/// ```
|
||||
pub fn double<F: Field>(&self) -> Value<Assigned<F>>
|
||||
where
|
||||
V: Borrow<Assigned<F>>,
|
||||
{
|
||||
Value {
|
||||
inner: self.inner.as_ref().map(|v| v.borrow().double()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Squares this field element.
|
||||
pub fn square<F: Field>(&self) -> Value<Assigned<F>>
|
||||
where
|
||||
V: Borrow<Assigned<F>>,
|
||||
{
|
||||
Value {
|
||||
inner: self.inner.as_ref().map(|v| v.borrow().square()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Cubes this field element.
|
||||
pub fn cube<F: Field>(&self) -> Value<Assigned<F>>
|
||||
where
|
||||
V: Borrow<Assigned<F>>,
|
||||
{
|
||||
Value {
|
||||
inner: self.inner.as_ref().map(|v| v.borrow().cube()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Inverts this assigned value (taking the inverse of zero to be zero).
|
||||
pub fn invert<F: Field>(&self) -> Value<Assigned<F>>
|
||||
where
|
||||
V: Borrow<Assigned<F>>,
|
||||
{
|
||||
Value {
|
||||
inner: self.inner.as_ref().map(|v| v.borrow().invert()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Value<Assigned<F>> {
|
||||
/// Evaluates this value directly, performing an unbatched inversion if necessary.
|
||||
///
|
||||
/// If the denominator is zero, the returned value is zero.
|
||||
pub fn evaluate(self) -> Value<F> {
|
||||
Value {
|
||||
inner: self.inner.map(|v| v.evaluate()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ use ff::Field;
|
||||
use crate::plonk::Assigned;
|
||||
use crate::{
|
||||
arithmetic::{FieldExt, Group},
|
||||
circuit,
|
||||
plonk::{
|
||||
permutation, Advice, Any, Assignment, Circuit, Column, ColumnType, ConstraintSystem, Error,
|
||||
Expression, Fixed, FloorPlanner, Instance, Selector, VirtualCell,
|
||||
@@ -167,7 +168,7 @@ impl<F: Group + Field> Mul<F> for Value<F> {
|
||||
/// ```
|
||||
/// use halo2_proofs::{
|
||||
/// arithmetic::FieldExt,
|
||||
/// circuit::{Layouter, SimpleFloorPlanner},
|
||||
/// circuit::{Layouter, SimpleFloorPlanner, Value},
|
||||
/// dev::{FailureLocation, MockProver, VerifyFailure},
|
||||
/// pasta::Fp,
|
||||
/// plonk::{Advice, Any, Circuit, Column, ConstraintSystem, Error, Selector},
|
||||
@@ -185,8 +186,8 @@ impl<F: Group + Field> Mul<F> for Value<F> {
|
||||
///
|
||||
/// #[derive(Clone, Default)]
|
||||
/// struct MyCircuit {
|
||||
/// a: Option<u64>,
|
||||
/// b: Option<u64>,
|
||||
/// a: Value<u64>,
|
||||
/// b: Value<u64>,
|
||||
/// }
|
||||
///
|
||||
/// impl<F: FieldExt> Circuit<F> for MyCircuit {
|
||||
@@ -220,15 +221,13 @@ impl<F: Group + Field> Mul<F> for Value<F> {
|
||||
/// layouter.assign_region(|| "Example region", |mut region| {
|
||||
/// config.s.enable(&mut region, 0)?;
|
||||
/// region.assign_advice(|| "a", config.a, 0, || {
|
||||
/// self.a.map(|v| F::from(v)).ok_or(Error::Synthesis)
|
||||
/// self.a.map(F::from)
|
||||
/// })?;
|
||||
/// region.assign_advice(|| "b", config.b, 0, || {
|
||||
/// self.b.map(|v| F::from(v)).ok_or(Error::Synthesis)
|
||||
/// self.b.map(F::from)
|
||||
/// })?;
|
||||
/// region.assign_advice(|| "c", config.c, 0, || {
|
||||
/// self.a
|
||||
/// .and_then(|a| self.b.map(|b| F::from(a * b)))
|
||||
/// .ok_or(Error::Synthesis)
|
||||
/// (self.a * self.b).map(F::from)
|
||||
/// })?;
|
||||
/// Ok(())
|
||||
/// })
|
||||
@@ -237,8 +236,8 @@ impl<F: Group + Field> Mul<F> for Value<F> {
|
||||
///
|
||||
/// // Assemble the private inputs to the circuit.
|
||||
/// let circuit = MyCircuit {
|
||||
/// a: Some(2),
|
||||
/// b: Some(4),
|
||||
/// a: Value::known(2),
|
||||
/// b: Value::known(4),
|
||||
/// };
|
||||
///
|
||||
/// // This circuit has no public inputs.
|
||||
@@ -340,7 +339,11 @@ impl<F: Field + Group> Assignment<F> for MockProver<F> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn query_instance(&self, column: Column<Instance>, row: usize) -> Result<Option<F>, Error> {
|
||||
fn query_instance(
|
||||
&self,
|
||||
column: Column<Instance>,
|
||||
row: usize,
|
||||
) -> Result<circuit::Value<F>, Error> {
|
||||
if !self.usable_rows.contains(&row) {
|
||||
return Err(Error::not_enough_rows_available(self.k));
|
||||
}
|
||||
@@ -348,7 +351,7 @@ impl<F: Field + Group> Assignment<F> for MockProver<F> {
|
||||
self.instance
|
||||
.get(column.index())
|
||||
.and_then(|column| column.get(row))
|
||||
.map(|v| Some(*v))
|
||||
.map(|v| circuit::Value::known(*v))
|
||||
.ok_or(Error::BoundsFailure)
|
||||
}
|
||||
|
||||
@@ -360,7 +363,7 @@ impl<F: Field + Group> Assignment<F> for MockProver<F> {
|
||||
to: V,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
V: FnOnce() -> Result<VR, Error>,
|
||||
V: FnOnce() -> circuit::Value<VR>,
|
||||
VR: Into<Assigned<F>>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
@@ -378,7 +381,8 @@ impl<F: Field + Group> Assignment<F> for MockProver<F> {
|
||||
.advice
|
||||
.get_mut(column.index())
|
||||
.and_then(|v| v.get_mut(row))
|
||||
.ok_or(Error::BoundsFailure)? = CellValue::Assigned(to()?.into().evaluate());
|
||||
.ok_or(Error::BoundsFailure)? =
|
||||
CellValue::Assigned(to().into_field().evaluate().assign()?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -391,7 +395,7 @@ impl<F: Field + Group> Assignment<F> for MockProver<F> {
|
||||
to: V,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
V: FnOnce() -> Result<VR, Error>,
|
||||
V: FnOnce() -> circuit::Value<VR>,
|
||||
VR: Into<Assigned<F>>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
@@ -409,7 +413,8 @@ impl<F: Field + Group> Assignment<F> for MockProver<F> {
|
||||
.fixed
|
||||
.get_mut(column.index())
|
||||
.and_then(|v| v.get_mut(row))
|
||||
.ok_or(Error::BoundsFailure)? = CellValue::Assigned(to()?.into().evaluate());
|
||||
.ok_or(Error::BoundsFailure)? =
|
||||
CellValue::Assigned(to().into_field().evaluate().assign()?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -433,14 +438,14 @@ impl<F: Field + Group> Assignment<F> for MockProver<F> {
|
||||
&mut self,
|
||||
col: Column<Fixed>,
|
||||
from_row: usize,
|
||||
to: Option<Assigned<F>>,
|
||||
to: circuit::Value<Assigned<F>>,
|
||||
) -> Result<(), Error> {
|
||||
if !self.usable_rows.contains(&from_row) {
|
||||
return Err(Error::not_enough_rows_available(self.k));
|
||||
}
|
||||
|
||||
for row in self.usable_rows.clone().skip(from_row) {
|
||||
self.assign_fixed(|| "", col, row, || to.ok_or(Error::Synthesis))?;
|
||||
self.assign_fixed(|| "", col, row, || to)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -876,7 +881,7 @@ mod tests {
|
||||
|
||||
use super::{FailureLocation, MockProver, VerifyFailure};
|
||||
use crate::{
|
||||
circuit::{Layouter, SimpleFloorPlanner},
|
||||
circuit::{Layouter, SimpleFloorPlanner, Value},
|
||||
plonk::{
|
||||
Advice, Any, Circuit, Column, ConstraintSystem, Error, Expression, Selector,
|
||||
TableColumn,
|
||||
@@ -933,7 +938,7 @@ mod tests {
|
||||
config.q.enable(&mut region, 1)?;
|
||||
|
||||
// Assign a = 0.
|
||||
region.assign_advice(|| "a", config.a, 0, || Ok(Fp::zero()))?;
|
||||
region.assign_advice(|| "a", config.a, 0, || Value::known(Fp::zero()))?;
|
||||
|
||||
// BUG: Forget to assign b = 0! This could go unnoticed during
|
||||
// development, because cell values default to zero, which in this
|
||||
@@ -1011,7 +1016,7 @@ mod tests {
|
||||
|| format!("table[{}] = {}", i, 2 * i),
|
||||
config.table,
|
||||
i - 1,
|
||||
|| Ok(Fp::from(2 * i as u64)),
|
||||
|| Value::known(Fp::from(2 * i as u64)),
|
||||
)
|
||||
})
|
||||
.fold(Ok(()), |acc, res| acc.and(res))
|
||||
@@ -1026,8 +1031,18 @@ mod tests {
|
||||
config.q.enable(&mut region, 1)?;
|
||||
|
||||
// Assign a = 2 and a = 6.
|
||||
region.assign_advice(|| "a = 2", config.a, 0, || Ok(Fp::from(2)))?;
|
||||
region.assign_advice(|| "a = 6", config.a, 1, || Ok(Fp::from(6)))?;
|
||||
region.assign_advice(
|
||||
|| "a = 2",
|
||||
config.a,
|
||||
0,
|
||||
|| Value::known(Fp::from(2)),
|
||||
)?;
|
||||
region.assign_advice(
|
||||
|| "a = 6",
|
||||
config.a,
|
||||
1,
|
||||
|| Value::known(Fp::from(6)),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
},
|
||||
@@ -1041,10 +1056,20 @@ mod tests {
|
||||
config.q.enable(&mut region, 1)?;
|
||||
|
||||
// Assign a = 4.
|
||||
region.assign_advice(|| "a = 4", config.a, 0, || Ok(Fp::from(4)))?;
|
||||
region.assign_advice(
|
||||
|| "a = 4",
|
||||
config.a,
|
||||
0,
|
||||
|| Value::known(Fp::from(4)),
|
||||
)?;
|
||||
|
||||
// BUG: Assign a = 5, which doesn't exist in the table!
|
||||
region.assign_advice(|| "a = 5", config.a, 1, || Ok(Fp::from(5)))?;
|
||||
region.assign_advice(
|
||||
|| "a = 5",
|
||||
config.a,
|
||||
1,
|
||||
|| Value::known(Fp::from(5)),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
},
|
||||
|
||||
@@ -11,6 +11,7 @@ use ff::{Field, PrimeField};
|
||||
use group::prime::PrimeGroup;
|
||||
|
||||
use crate::{
|
||||
circuit::Value,
|
||||
plonk::{
|
||||
Advice, Any, Assigned, Assignment, Circuit, Column, ConstraintSystem, Error, Fixed,
|
||||
FloorPlanner, Instance, Selector,
|
||||
@@ -68,8 +69,8 @@ impl<F: Field> Assignment<F> for Assembly {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn query_instance(&self, _: Column<Instance>, _: usize) -> Result<Option<F>, Error> {
|
||||
Ok(None)
|
||||
fn query_instance(&self, _: Column<Instance>, _: usize) -> Result<Value<F>, Error> {
|
||||
Ok(Value::unknown())
|
||||
}
|
||||
|
||||
fn assign_advice<V, VR, A, AR>(
|
||||
@@ -80,7 +81,7 @@ impl<F: Field> Assignment<F> for Assembly {
|
||||
_: V,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
V: FnOnce() -> Result<VR, Error>,
|
||||
V: FnOnce() -> Value<VR>,
|
||||
VR: Into<Assigned<F>>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
@@ -96,7 +97,7 @@ impl<F: Field> Assignment<F> for Assembly {
|
||||
_: V,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
V: FnOnce() -> Result<VR, Error>,
|
||||
V: FnOnce() -> Value<VR>,
|
||||
VR: Into<Assigned<F>>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
@@ -112,7 +113,7 @@ impl<F: Field> Assignment<F> for Assembly {
|
||||
&mut self,
|
||||
_: Column<Fixed>,
|
||||
_: usize,
|
||||
_: Option<Assigned<F>>,
|
||||
_: Value<Assigned<F>>,
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
use ff::Field;
|
||||
use tabbycat::{AttrList, Edge, GraphBuilder, GraphType, Identity, StmtList};
|
||||
|
||||
use crate::plonk::{
|
||||
Advice, Any, Assigned, Assignment, Circuit, Column, ConstraintSystem, Error, Fixed,
|
||||
FloorPlanner, Instance, Selector,
|
||||
use crate::{
|
||||
circuit::Value,
|
||||
plonk::{
|
||||
Advice, Any, Assigned, Assignment, Circuit, Column, ConstraintSystem, Error, Fixed,
|
||||
FloorPlanner, Instance, Selector,
|
||||
},
|
||||
};
|
||||
|
||||
pub mod layout;
|
||||
@@ -96,8 +99,8 @@ impl<F: Field> Assignment<F> for Graph {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn query_instance(&self, _: Column<Instance>, _: usize) -> Result<Option<F>, Error> {
|
||||
Ok(None)
|
||||
fn query_instance(&self, _: Column<Instance>, _: usize) -> Result<Value<F>, Error> {
|
||||
Ok(Value::unknown())
|
||||
}
|
||||
|
||||
fn assign_advice<V, VR, A, AR>(
|
||||
@@ -108,7 +111,7 @@ impl<F: Field> Assignment<F> for Graph {
|
||||
_: V,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
V: FnOnce() -> Result<VR, Error>,
|
||||
V: FnOnce() -> Value<VR>,
|
||||
VR: Into<Assigned<F>>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
@@ -125,7 +128,7 @@ impl<F: Field> Assignment<F> for Graph {
|
||||
_: V,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
V: FnOnce() -> Result<VR, Error>,
|
||||
V: FnOnce() -> Value<VR>,
|
||||
VR: Into<Assigned<F>>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
@@ -149,7 +152,7 @@ impl<F: Field> Assignment<F> for Graph {
|
||||
&mut self,
|
||||
_: Column<Fixed>,
|
||||
_: usize,
|
||||
_: Option<Assigned<F>>,
|
||||
_: Value<Assigned<F>>,
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -7,10 +7,12 @@ use std::cmp;
|
||||
use std::collections::HashSet;
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::circuit::layouter::RegionColumn;
|
||||
use crate::plonk::{
|
||||
Advice, Any, Assigned, Assignment, Circuit, Column, ConstraintSystem, Error, Fixed,
|
||||
FloorPlanner, Instance, Selector,
|
||||
use crate::{
|
||||
circuit::{layouter::RegionColumn, Value},
|
||||
plonk::{
|
||||
Advice, Any, Assigned, Assignment, Circuit, Column, ConstraintSystem, Error, Fixed,
|
||||
FloorPlanner, Instance, Selector,
|
||||
},
|
||||
};
|
||||
|
||||
/// Graphical renderer for circuit layouts.
|
||||
@@ -430,8 +432,8 @@ impl<F: Field> Assignment<F> for Layout {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn query_instance(&self, _: Column<Instance>, _: usize) -> Result<Option<F>, Error> {
|
||||
Ok(None)
|
||||
fn query_instance(&self, _: Column<Instance>, _: usize) -> Result<Value<F>, Error> {
|
||||
Ok(Value::unknown())
|
||||
}
|
||||
|
||||
fn assign_advice<V, VR, A, AR>(
|
||||
@@ -442,7 +444,7 @@ impl<F: Field> Assignment<F> for Layout {
|
||||
_: V,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
V: FnOnce() -> Result<VR, Error>,
|
||||
V: FnOnce() -> Value<VR>,
|
||||
VR: Into<Assigned<F>>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
@@ -459,7 +461,7 @@ impl<F: Field> Assignment<F> for Layout {
|
||||
_: V,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
V: FnOnce() -> Result<VR, Error>,
|
||||
V: FnOnce() -> Value<VR>,
|
||||
VR: Into<Assigned<F>>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
@@ -483,7 +485,7 @@ impl<F: Field> Assignment<F> for Layout {
|
||||
&mut self,
|
||||
_: Column<Fixed>,
|
||||
_: usize,
|
||||
_: Option<Assigned<F>>,
|
||||
_: Value<Assigned<F>>,
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -250,6 +250,13 @@ impl<F: Field> Mul<F> for Assigned<F> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Mul<F> for &Assigned<F> {
|
||||
type Output = Assigned<F>;
|
||||
fn mul(self, rhs: F) -> Assigned<F> {
|
||||
*self * rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Field> Mul<&Assigned<F>> for Assigned<F> {
|
||||
type Output = Assigned<F>;
|
||||
fn mul(self, rhs: &Assigned<F>) -> Assigned<F> {
|
||||
|
||||
@@ -7,8 +7,10 @@ use std::{
|
||||
};
|
||||
|
||||
use super::{lookup, permutation, Assigned, Error};
|
||||
use crate::circuit::Layouter;
|
||||
use crate::{circuit::Region, poly::Rotation};
|
||||
use crate::{
|
||||
circuit::{Layouter, Region, Value},
|
||||
poly::Rotation,
|
||||
};
|
||||
|
||||
mod compress_selectors;
|
||||
|
||||
@@ -226,7 +228,11 @@ impl TryFrom<Column<Any>> for Column<Instance> {
|
||||
/// Selectors are disabled on all rows by default, and must be explicitly enabled on each
|
||||
/// row when required:
|
||||
/// ```
|
||||
/// use halo2_proofs::{arithmetic::FieldExt, circuit::{Chip, Layouter}, plonk::{Advice, Column, Error, Selector}};
|
||||
/// use halo2_proofs::{
|
||||
/// arithmetic::FieldExt,
|
||||
/// circuit::{Chip, Layouter, Value},
|
||||
/// plonk::{Advice, Column, Error, Selector},
|
||||
/// };
|
||||
/// # use ff::Field;
|
||||
/// # use halo2_proofs::plonk::Fixed;
|
||||
///
|
||||
@@ -240,8 +246,8 @@ impl TryFrom<Column<Any>> for Column<Instance> {
|
||||
/// let config = chip.config();
|
||||
/// # let config: Config = todo!();
|
||||
/// layouter.assign_region(|| "bar", |mut region| {
|
||||
/// region.assign_advice(|| "a", config.a, 0, || Ok(F::one()))?;
|
||||
/// region.assign_advice(|| "a", config.b, 1, || Ok(F::one()))?;
|
||||
/// region.assign_advice(|| "a", config.a, 0, || Value::known(F::one()))?;
|
||||
/// region.assign_advice(|| "a", config.b, 1, || Value::known(F::one()))?;
|
||||
/// config.s.enable(&mut region, 1)
|
||||
/// })?;
|
||||
/// Ok(())
|
||||
@@ -329,7 +335,7 @@ pub trait Assignment<F: Field> {
|
||||
/// Queries the cell of an instance column at a particular absolute row.
|
||||
///
|
||||
/// Returns the cell's value, if known.
|
||||
fn query_instance(&self, column: Column<Instance>, row: usize) -> Result<Option<F>, Error>;
|
||||
fn query_instance(&self, column: Column<Instance>, row: usize) -> Result<Value<F>, Error>;
|
||||
|
||||
/// Assign an advice column value (witness)
|
||||
fn assign_advice<V, VR, A, AR>(
|
||||
@@ -340,7 +346,7 @@ pub trait Assignment<F: Field> {
|
||||
to: V,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
V: FnOnce() -> Result<VR, Error>,
|
||||
V: FnOnce() -> Value<VR>,
|
||||
VR: Into<Assigned<F>>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>;
|
||||
@@ -354,7 +360,7 @@ pub trait Assignment<F: Field> {
|
||||
to: V,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
V: FnOnce() -> Result<VR, Error>,
|
||||
V: FnOnce() -> Value<VR>,
|
||||
VR: Into<Assigned<F>>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>;
|
||||
@@ -373,7 +379,7 @@ pub trait Assignment<F: Field> {
|
||||
&mut self,
|
||||
column: Column<Fixed>,
|
||||
row: usize,
|
||||
to: Option<Assigned<F>>,
|
||||
to: Value<Assigned<F>>,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// Creates a new (sub)namespace and enters into it.
|
||||
|
||||
@@ -12,11 +12,15 @@ use super::{
|
||||
},
|
||||
permutation, Assigned, Error, LagrangeCoeff, Polynomial, ProvingKey, VerifyingKey,
|
||||
};
|
||||
use crate::poly::{
|
||||
commitment::{Blind, Params},
|
||||
EvaluationDomain,
|
||||
use crate::{
|
||||
arithmetic::CurveAffine,
|
||||
circuit::Value,
|
||||
poly::{
|
||||
batch_invert_assigned,
|
||||
commitment::{Blind, Params},
|
||||
EvaluationDomain,
|
||||
},
|
||||
};
|
||||
use crate::{arithmetic::CurveAffine, poly::batch_invert_assigned};
|
||||
|
||||
pub(crate) fn create_domain<C, ConcreteCircuit>(
|
||||
params: &Params<C>,
|
||||
@@ -78,13 +82,13 @@ impl<F: Field> Assignment<F> for Assembly<F> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn query_instance(&self, _: Column<Instance>, row: usize) -> Result<Option<F>, Error> {
|
||||
fn query_instance(&self, _: Column<Instance>, row: usize) -> Result<Value<F>, Error> {
|
||||
if !self.usable_rows.contains(&row) {
|
||||
return Err(Error::not_enough_rows_available(self.k));
|
||||
}
|
||||
|
||||
// There is no instance in this context.
|
||||
Ok(None)
|
||||
Ok(Value::unknown())
|
||||
}
|
||||
|
||||
fn assign_advice<V, VR, A, AR>(
|
||||
@@ -95,7 +99,7 @@ impl<F: Field> Assignment<F> for Assembly<F> {
|
||||
_: V,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
V: FnOnce() -> Result<VR, Error>,
|
||||
V: FnOnce() -> Value<VR>,
|
||||
VR: Into<Assigned<F>>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
@@ -112,7 +116,7 @@ impl<F: Field> Assignment<F> for Assembly<F> {
|
||||
to: V,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
V: FnOnce() -> Result<VR, Error>,
|
||||
V: FnOnce() -> Value<VR>,
|
||||
VR: Into<Assigned<F>>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
@@ -125,7 +129,7 @@ impl<F: Field> Assignment<F> for Assembly<F> {
|
||||
.fixed
|
||||
.get_mut(column.index())
|
||||
.and_then(|v| v.get_mut(row))
|
||||
.ok_or(Error::BoundsFailure)? = to()?.into();
|
||||
.ok_or(Error::BoundsFailure)? = to().into_field().assign()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -149,7 +153,7 @@ impl<F: Field> Assignment<F> for Assembly<F> {
|
||||
&mut self,
|
||||
column: Column<Fixed>,
|
||||
from_row: usize,
|
||||
to: Option<Assigned<F>>,
|
||||
to: Value<Assigned<F>>,
|
||||
) -> Result<(), Error> {
|
||||
if !self.usable_rows.contains(&from_row) {
|
||||
return Err(Error::not_enough_rows_available(self.k));
|
||||
@@ -160,8 +164,9 @@ impl<F: Field> Assignment<F> for Assembly<F> {
|
||||
.get_mut(column.index())
|
||||
.ok_or(Error::BoundsFailure)?;
|
||||
|
||||
let filler = to.assign()?;
|
||||
for row in self.usable_rows.clone().skip(from_row) {
|
||||
col[row] = to.ok_or(Error::Synthesis)?;
|
||||
col[row] = filler;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -12,15 +12,16 @@ use super::{
|
||||
lookup, permutation, vanishing, ChallengeBeta, ChallengeGamma, ChallengeTheta, ChallengeX,
|
||||
ChallengeY, Error, ProvingKey,
|
||||
};
|
||||
use crate::poly::{
|
||||
self,
|
||||
commitment::{Blind, Params},
|
||||
multiopen::{self, ProverQuery},
|
||||
Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial,
|
||||
};
|
||||
use crate::{
|
||||
arithmetic::{eval_polynomial, CurveAffine, FieldExt},
|
||||
circuit::Value,
|
||||
plonk::Assigned,
|
||||
poly::{
|
||||
self,
|
||||
commitment::{Blind, Params},
|
||||
multiopen::{self, ProverQuery},
|
||||
Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial,
|
||||
},
|
||||
};
|
||||
use crate::{
|
||||
poly::batch_invert_assigned,
|
||||
@@ -171,7 +172,7 @@ pub fn create_proof<
|
||||
&self,
|
||||
column: Column<Instance>,
|
||||
row: usize,
|
||||
) -> Result<Option<F>, Error> {
|
||||
) -> Result<Value<F>, Error> {
|
||||
if !self.usable_rows.contains(&row) {
|
||||
return Err(Error::not_enough_rows_available(self.k));
|
||||
}
|
||||
@@ -179,7 +180,7 @@ pub fn create_proof<
|
||||
self.instances
|
||||
.get(column.index())
|
||||
.and_then(|column| column.get(row))
|
||||
.map(|v| Some(*v))
|
||||
.map(|v| Value::known(*v))
|
||||
.ok_or(Error::BoundsFailure)
|
||||
}
|
||||
|
||||
@@ -191,7 +192,7 @@ pub fn create_proof<
|
||||
to: V,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
V: FnOnce() -> Result<VR, Error>,
|
||||
V: FnOnce() -> Value<VR>,
|
||||
VR: Into<Assigned<F>>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
@@ -204,7 +205,7 @@ pub fn create_proof<
|
||||
.advice
|
||||
.get_mut(column.index())
|
||||
.and_then(|v| v.get_mut(row))
|
||||
.ok_or(Error::BoundsFailure)? = to()?.into();
|
||||
.ok_or(Error::BoundsFailure)? = to().into_field().assign()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -217,7 +218,7 @@ pub fn create_proof<
|
||||
_: V,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
V: FnOnce() -> Result<VR, Error>,
|
||||
V: FnOnce() -> Value<VR>,
|
||||
VR: Into<Assigned<F>>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
@@ -243,7 +244,7 @@ pub fn create_proof<
|
||||
&mut self,
|
||||
_: Column<Fixed>,
|
||||
_: usize,
|
||||
_: Option<Assigned<F>>,
|
||||
_: Value<Assigned<F>>,
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
|
||||
use assert_matches::assert_matches;
|
||||
use halo2_proofs::arithmetic::{CurveAffine, FieldExt};
|
||||
use halo2_proofs::circuit::{Cell, Layouter, SimpleFloorPlanner};
|
||||
use halo2_proofs::circuit::{Cell, Layouter, SimpleFloorPlanner, Value};
|
||||
use halo2_proofs::dev::MockProver;
|
||||
use halo2_proofs::pasta::{Eq, EqAffine, Fp};
|
||||
use halo2_proofs::plonk::{
|
||||
create_proof, keygen_pk, keygen_vk, verify_proof, Advice, BatchVerifier, Circuit, Column,
|
||||
ConstraintSystem, Error, Fixed, SingleVerifier, TableColumn, VerificationStrategy,
|
||||
create_proof, keygen_pk, keygen_vk, verify_proof, Advice, Assigned, BatchVerifier, Circuit,
|
||||
Column, ConstraintSystem, Error, Fixed, SingleVerifier, TableColumn, VerificationStrategy,
|
||||
};
|
||||
use halo2_proofs::poly::commitment::{Guard, MSM};
|
||||
use halo2_proofs::poly::{commitment::Params, Rotation};
|
||||
@@ -51,18 +51,18 @@ fn plonk_api() {
|
||||
f: F,
|
||||
) -> Result<(Cell, Cell, Cell), Error>
|
||||
where
|
||||
F: FnMut() -> Result<(FF, FF, FF), Error>;
|
||||
F: FnMut() -> Value<(Assigned<FF>, Assigned<FF>, Assigned<FF>)>;
|
||||
fn raw_add<F>(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<FF>,
|
||||
f: F,
|
||||
) -> Result<(Cell, Cell, Cell), Error>
|
||||
where
|
||||
F: FnMut() -> Result<(FF, FF, FF), Error>;
|
||||
F: FnMut() -> Value<(Assigned<FF>, Assigned<FF>, Assigned<FF>)>;
|
||||
fn copy(&self, layouter: &mut impl Layouter<FF>, a: Cell, b: Cell) -> Result<(), Error>;
|
||||
fn public_input<F>(&self, layouter: &mut impl Layouter<FF>, f: F) -> Result<Cell, Error>
|
||||
where
|
||||
F: FnMut() -> Result<FF, Error>;
|
||||
F: FnMut() -> Value<FF>;
|
||||
fn lookup_table(
|
||||
&self,
|
||||
layouter: &mut impl Layouter<FF>,
|
||||
@@ -72,7 +72,7 @@ fn plonk_api() {
|
||||
|
||||
#[derive(Clone)]
|
||||
struct MyCircuit<F: FieldExt> {
|
||||
a: Option<F>,
|
||||
a: Value<F>,
|
||||
lookup_table: Vec<F>,
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ fn plonk_api() {
|
||||
mut f: F,
|
||||
) -> Result<(Cell, Cell, Cell), Error>
|
||||
where
|
||||
F: FnMut() -> Result<(FF, FF, FF), Error>,
|
||||
F: FnMut() -> Value<(Assigned<FF>, Assigned<FF>, Assigned<FF>)>,
|
||||
{
|
||||
layouter.assign_region(
|
||||
|| "raw_multiply",
|
||||
@@ -108,39 +108,44 @@ fn plonk_api() {
|
||||
self.config.a,
|
||||
0,
|
||||
|| {
|
||||
value = Some(f()?);
|
||||
Ok(value.ok_or(Error::Synthesis)?.0)
|
||||
value = Some(f());
|
||||
value.unwrap().map(|v| v.0)
|
||||
},
|
||||
)?;
|
||||
region.assign_advice(
|
||||
|| "lhs^4",
|
||||
self.config.d,
|
||||
0,
|
||||
|| Ok(value.ok_or(Error::Synthesis)?.0.square().square()),
|
||||
|| value.unwrap().map(|v| v.0).square().square(),
|
||||
)?;
|
||||
let rhs = region.assign_advice(
|
||||
|| "rhs",
|
||||
self.config.b,
|
||||
0,
|
||||
|| Ok(value.ok_or(Error::Synthesis)?.1),
|
||||
|| value.unwrap().map(|v| v.1),
|
||||
)?;
|
||||
region.assign_advice(
|
||||
|| "rhs^4",
|
||||
self.config.e,
|
||||
0,
|
||||
|| Ok(value.ok_or(Error::Synthesis)?.1.square().square()),
|
||||
|| value.unwrap().map(|v| v.1).square().square(),
|
||||
)?;
|
||||
let out = region.assign_advice(
|
||||
|| "out",
|
||||
self.config.c,
|
||||
0,
|
||||
|| Ok(value.ok_or(Error::Synthesis)?.2),
|
||||
|| value.unwrap().map(|v| v.2),
|
||||
)?;
|
||||
|
||||
region.assign_fixed(|| "a", self.config.sa, 0, || Ok(FF::zero()))?;
|
||||
region.assign_fixed(|| "b", self.config.sb, 0, || Ok(FF::zero()))?;
|
||||
region.assign_fixed(|| "c", self.config.sc, 0, || Ok(FF::one()))?;
|
||||
region.assign_fixed(|| "a * b", self.config.sm, 0, || Ok(FF::one()))?;
|
||||
region.assign_fixed(|| "a", self.config.sa, 0, || Value::known(FF::zero()))?;
|
||||
region.assign_fixed(|| "b", self.config.sb, 0, || Value::known(FF::zero()))?;
|
||||
region.assign_fixed(|| "c", self.config.sc, 0, || Value::known(FF::one()))?;
|
||||
region.assign_fixed(
|
||||
|| "a * b",
|
||||
self.config.sm,
|
||||
0,
|
||||
|| Value::known(FF::one()),
|
||||
)?;
|
||||
Ok((lhs.cell(), rhs.cell(), out.cell()))
|
||||
},
|
||||
)
|
||||
@@ -151,7 +156,7 @@ fn plonk_api() {
|
||||
mut f: F,
|
||||
) -> Result<(Cell, Cell, Cell), Error>
|
||||
where
|
||||
F: FnMut() -> Result<(FF, FF, FF), Error>,
|
||||
F: FnMut() -> Value<(Assigned<FF>, Assigned<FF>, Assigned<FF>)>,
|
||||
{
|
||||
layouter.assign_region(
|
||||
|| "raw_add",
|
||||
@@ -162,39 +167,44 @@ fn plonk_api() {
|
||||
self.config.a,
|
||||
0,
|
||||
|| {
|
||||
value = Some(f()?);
|
||||
Ok(value.ok_or(Error::Synthesis)?.0)
|
||||
value = Some(f());
|
||||
value.unwrap().map(|v| v.0)
|
||||
},
|
||||
)?;
|
||||
region.assign_advice(
|
||||
|| "lhs^4",
|
||||
self.config.d,
|
||||
0,
|
||||
|| Ok(value.ok_or(Error::Synthesis)?.0.square().square()),
|
||||
|| value.unwrap().map(|v| v.0).square().square(),
|
||||
)?;
|
||||
let rhs = region.assign_advice(
|
||||
|| "rhs",
|
||||
self.config.b,
|
||||
0,
|
||||
|| Ok(value.ok_or(Error::Synthesis)?.1),
|
||||
|| value.unwrap().map(|v| v.1),
|
||||
)?;
|
||||
region.assign_advice(
|
||||
|| "rhs^4",
|
||||
self.config.e,
|
||||
0,
|
||||
|| Ok(value.ok_or(Error::Synthesis)?.1.square().square()),
|
||||
|| value.unwrap().map(|v| v.1).square().square(),
|
||||
)?;
|
||||
let out = region.assign_advice(
|
||||
|| "out",
|
||||
self.config.c,
|
||||
0,
|
||||
|| Ok(value.ok_or(Error::Synthesis)?.2),
|
||||
|| value.unwrap().map(|v| v.2),
|
||||
)?;
|
||||
|
||||
region.assign_fixed(|| "a", self.config.sa, 0, || Ok(FF::one()))?;
|
||||
region.assign_fixed(|| "b", self.config.sb, 0, || Ok(FF::one()))?;
|
||||
region.assign_fixed(|| "c", self.config.sc, 0, || Ok(FF::one()))?;
|
||||
region.assign_fixed(|| "a * b", self.config.sm, 0, || Ok(FF::zero()))?;
|
||||
region.assign_fixed(|| "a", self.config.sa, 0, || Value::known(FF::one()))?;
|
||||
region.assign_fixed(|| "b", self.config.sb, 0, || Value::known(FF::one()))?;
|
||||
region.assign_fixed(|| "c", self.config.sc, 0, || Value::known(FF::one()))?;
|
||||
region.assign_fixed(
|
||||
|| "a * b",
|
||||
self.config.sm,
|
||||
0,
|
||||
|| Value::known(FF::zero()),
|
||||
)?;
|
||||
Ok((lhs.cell(), rhs.cell(), out.cell()))
|
||||
},
|
||||
)
|
||||
@@ -215,13 +225,18 @@ fn plonk_api() {
|
||||
}
|
||||
fn public_input<F>(&self, layouter: &mut impl Layouter<FF>, mut f: F) -> Result<Cell, Error>
|
||||
where
|
||||
F: FnMut() -> Result<FF, Error>,
|
||||
F: FnMut() -> Value<FF>,
|
||||
{
|
||||
layouter.assign_region(
|
||||
|| "public_input",
|
||||
|mut region| {
|
||||
let value = region.assign_advice(|| "value", self.config.a, 0, &mut f)?;
|
||||
region.assign_fixed(|| "public", self.config.sp, 0, || Ok(FF::one()))?;
|
||||
region.assign_fixed(
|
||||
|| "public",
|
||||
self.config.sp,
|
||||
0,
|
||||
|| Value::known(FF::one()),
|
||||
)?;
|
||||
|
||||
Ok(value.cell())
|
||||
},
|
||||
@@ -236,7 +251,12 @@ fn plonk_api() {
|
||||
|| "",
|
||||
|mut table| {
|
||||
for (index, &value) in values.iter().enumerate() {
|
||||
table.assign_cell(|| "table col", self.config.sl, index, || Ok(value))?;
|
||||
table.assign_cell(
|
||||
|| "table col",
|
||||
self.config.sl,
|
||||
index,
|
||||
|| Value::known(value),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
@@ -251,7 +271,7 @@ fn plonk_api() {
|
||||
|
||||
fn without_witnesses(&self) -> Self {
|
||||
Self {
|
||||
a: None,
|
||||
a: Value::unknown(),
|
||||
lookup_table: self.lookup_table.clone(),
|
||||
}
|
||||
}
|
||||
@@ -353,25 +373,20 @@ fn plonk_api() {
|
||||
) -> Result<(), Error> {
|
||||
let cs = StandardPlonk::new(config);
|
||||
|
||||
let _ = cs.public_input(&mut layouter, || Ok(F::one() + F::one()))?;
|
||||
let _ = cs.public_input(&mut layouter, || Value::known(F::one() + F::one()))?;
|
||||
|
||||
for _ in 0..10 {
|
||||
let mut a_squared = None;
|
||||
let a: Value<Assigned<_>> = self.a.into();
|
||||
let mut a_squared = Value::unknown();
|
||||
let (a0, _, c0) = cs.raw_multiply(&mut layouter, || {
|
||||
a_squared = self.a.map(|a| a.square());
|
||||
Ok((
|
||||
self.a.ok_or(Error::Synthesis)?,
|
||||
self.a.ok_or(Error::Synthesis)?,
|
||||
a_squared.ok_or(Error::Synthesis)?,
|
||||
))
|
||||
a_squared = a.square();
|
||||
a.zip(a_squared).map(|(a, a_squared)| (a, a, a_squared))
|
||||
})?;
|
||||
let (a1, b1, _) = cs.raw_add(&mut layouter, || {
|
||||
let fin = a_squared.and_then(|a2| self.a.map(|a| a + a2));
|
||||
Ok((
|
||||
self.a.ok_or(Error::Synthesis)?,
|
||||
a_squared.ok_or(Error::Synthesis)?,
|
||||
fin.ok_or(Error::Synthesis)?,
|
||||
))
|
||||
let fin = a_squared + a;
|
||||
a.zip(a_squared)
|
||||
.zip(fin)
|
||||
.map(|((a, a_squared), fin)| (a, a_squared, fin))
|
||||
})?;
|
||||
cs.copy(&mut layouter, a0, a1)?;
|
||||
cs.copy(&mut layouter, b1, c0)?;
|
||||
@@ -388,12 +403,12 @@ fn plonk_api() {
|
||||
let lookup_table = vec![instance, a, a, Fp::zero()];
|
||||
|
||||
let empty_circuit: MyCircuit<Fp> = MyCircuit {
|
||||
a: None,
|
||||
a: Value::unknown(),
|
||||
lookup_table: lookup_table.clone(),
|
||||
};
|
||||
|
||||
let circuit: MyCircuit<Fp> = MyCircuit {
|
||||
a: Some(a),
|
||||
a: Value::known(a),
|
||||
lookup_table,
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user