mirror of https://github.com/zcash/halo2.git
utilities::cond_swap: Directly witness swap bit
This commit is contained in:
parent
f31b9feba0
commit
6603e996ed
|
@ -1,6 +1,6 @@
|
||||||
use super::{copy, CellValue, UtilitiesInstructions};
|
use super::{copy, CellValue, UtilitiesInstructions};
|
||||||
use halo2::{
|
use halo2::{
|
||||||
circuit::{Cell, Chip, Layouter},
|
circuit::{Chip, Layouter},
|
||||||
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Permutation, Selector},
|
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Permutation, Selector},
|
||||||
poly::Rotation,
|
poly::Rotation,
|
||||||
};
|
};
|
||||||
|
@ -11,9 +11,7 @@ pub trait CondSwapInstructions<F: FieldExt>: UtilitiesInstructions<F> {
|
||||||
/// Variable representing cell with a certain value in the circuit.
|
/// Variable representing cell with a certain value in the circuit.
|
||||||
type Var;
|
type Var;
|
||||||
|
|
||||||
/// Variable representing a `swap` boolean flag.
|
#[allow(clippy::type_complexity)]
|
||||||
type Swap: From<<Self as CondSwapInstructions<F>>::Var>;
|
|
||||||
|
|
||||||
/// Given an input pair (x,y) and a `swap` boolean flag, return
|
/// Given an input pair (x,y) and a `swap` boolean flag, return
|
||||||
/// (y,x) if `swap` is set, else (x,y) if `swap` is not set.
|
/// (y,x) if `swap` is set, else (x,y) if `swap` is not set.
|
||||||
fn swap(
|
fn swap(
|
||||||
|
@ -23,7 +21,7 @@ pub trait CondSwapInstructions<F: FieldExt>: UtilitiesInstructions<F> {
|
||||||
<Self as CondSwapInstructions<F>>::Var,
|
<Self as CondSwapInstructions<F>>::Var,
|
||||||
<Self as CondSwapInstructions<F>>::Var,
|
<Self as CondSwapInstructions<F>>::Var,
|
||||||
),
|
),
|
||||||
swap: Self::Swap,
|
swap: Option<bool>,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
(
|
(
|
||||||
<Self as CondSwapInstructions<F>>::Var,
|
<Self as CondSwapInstructions<F>>::Var,
|
||||||
|
@ -55,40 +53,13 @@ impl<F: FieldExt> Chip<F> for CondSwapChip<F> {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct CondSwapConfig {
|
pub struct CondSwapConfig {
|
||||||
q_swap: Selector,
|
pub q_swap: Selector,
|
||||||
x: Column<Advice>,
|
pub x: Column<Advice>,
|
||||||
y: Column<Advice>,
|
pub y: Column<Advice>,
|
||||||
x_swapped: Column<Advice>,
|
pub x_swapped: Column<Advice>,
|
||||||
y_swapped: Column<Advice>,
|
pub y_swapped: Column<Advice>,
|
||||||
swap: Column<Advice>,
|
pub swap: Column<Advice>,
|
||||||
perm: Permutation,
|
pub perm: Permutation,
|
||||||
}
|
|
||||||
|
|
||||||
/// A variable representing a `swap` boolean flag.
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Swap {
|
|
||||||
cell: Cell,
|
|
||||||
value: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: FieldExt> From<CellValue<F>> for Swap {
|
|
||||||
fn from(var: CellValue<F>) -> Self {
|
|
||||||
let value = var.value.map(|value| {
|
|
||||||
let zero = value == F::zero();
|
|
||||||
let one = value == F::one();
|
|
||||||
if zero {
|
|
||||||
false
|
|
||||||
} else if one {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
panic!("Value must be boolean.")
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Swap {
|
|
||||||
cell: var.cell,
|
|
||||||
value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: FieldExt> UtilitiesInstructions<F> for CondSwapChip<F> {
|
impl<F: FieldExt> UtilitiesInstructions<F> for CondSwapChip<F> {
|
||||||
|
@ -97,8 +68,8 @@ impl<F: FieldExt> UtilitiesInstructions<F> for CondSwapChip<F> {
|
||||||
|
|
||||||
impl<F: FieldExt> CondSwapInstructions<F> for CondSwapChip<F> {
|
impl<F: FieldExt> CondSwapInstructions<F> for CondSwapChip<F> {
|
||||||
type Var = CellValue<F>;
|
type Var = CellValue<F>;
|
||||||
type Swap = Swap;
|
|
||||||
|
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
fn swap(
|
fn swap(
|
||||||
&self,
|
&self,
|
||||||
mut layouter: impl Layouter<F>,
|
mut layouter: impl Layouter<F>,
|
||||||
|
@ -106,7 +77,7 @@ impl<F: FieldExt> CondSwapInstructions<F> for CondSwapChip<F> {
|
||||||
<Self as CondSwapInstructions<F>>::Var,
|
<Self as CondSwapInstructions<F>>::Var,
|
||||||
<Self as CondSwapInstructions<F>>::Var,
|
<Self as CondSwapInstructions<F>>::Var,
|
||||||
),
|
),
|
||||||
swap: Self::Swap,
|
swap: Option<bool>,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
(
|
(
|
||||||
<Self as CondSwapInstructions<F>>::Var,
|
<Self as CondSwapInstructions<F>>::Var,
|
||||||
|
@ -128,27 +99,22 @@ impl<F: FieldExt> CondSwapInstructions<F> for CondSwapChip<F> {
|
||||||
// Copy in `y` value
|
// Copy in `y` value
|
||||||
let y = copy(&mut region, || "copy y", config.y, 0, &pair.1, &config.perm)?;
|
let y = copy(&mut region, || "copy y", config.y, 0, &pair.1, &config.perm)?;
|
||||||
|
|
||||||
// Copy in `swap` value
|
// Witness `swap` value
|
||||||
let swap_val = swap.value;
|
let swap_val = swap.map(|swap| F::from_u64(swap as u64));
|
||||||
let swap_cell = region.assign_advice(
|
region.assign_advice(
|
||||||
|| "swap",
|
|| "swap",
|
||||||
config.swap,
|
config.swap,
|
||||||
0,
|
0,
|
||||||
|| {
|
|| swap_val.ok_or(Error::SynthesisError),
|
||||||
swap_val
|
|
||||||
.map(|swap| F::from_u64(swap as u64))
|
|
||||||
.ok_or(Error::SynthesisError)
|
|
||||||
},
|
|
||||||
)?;
|
)?;
|
||||||
region.constrain_equal(&config.perm, swap_cell, swap.cell)?;
|
|
||||||
|
|
||||||
// Conditionally swap x
|
// Conditionally swap x
|
||||||
let x_swapped = {
|
let x_swapped = {
|
||||||
let x_swapped =
|
let x_swapped = x
|
||||||
x.value
|
.value
|
||||||
.zip(y.value)
|
.zip(y.value)
|
||||||
.zip(swap_val)
|
.zip(swap)
|
||||||
.map(|((x, y), swap)| if swap { y } else { x });
|
.map(|((x, y), swap)| if swap { y } else { x });
|
||||||
let x_swapped_cell = region.assign_advice(
|
let x_swapped_cell = region.assign_advice(
|
||||||
|| "x_swapped",
|
|| "x_swapped",
|
||||||
config.x_swapped,
|
config.x_swapped,
|
||||||
|
@ -163,11 +129,11 @@ impl<F: FieldExt> CondSwapInstructions<F> for CondSwapChip<F> {
|
||||||
|
|
||||||
// Conditionally swap y
|
// Conditionally swap y
|
||||||
let y_swapped = {
|
let y_swapped = {
|
||||||
let y_swapped =
|
let y_swapped = x
|
||||||
x.value
|
.value
|
||||||
.zip(y.value)
|
.zip(y.value)
|
||||||
.zip(swap_val)
|
.zip(swap)
|
||||||
.map(|((x, y), swap)| if swap { x } else { y });
|
.map(|((x, y), swap)| if swap { x } else { y });
|
||||||
let y_swapped_cell = region.assign_advice(
|
let y_swapped_cell = region.assign_advice(
|
||||||
|| "y_swapped",
|
|| "y_swapped",
|
||||||
config.y_swapped,
|
config.y_swapped,
|
||||||
|
@ -266,7 +232,7 @@ mod tests {
|
||||||
struct MyCircuit<F: FieldExt> {
|
struct MyCircuit<F: FieldExt> {
|
||||||
x: Option<F>,
|
x: Option<F>,
|
||||||
y: Option<F>,
|
y: Option<F>,
|
||||||
swap: Option<F>,
|
swap: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
|
impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
|
||||||
|
@ -302,15 +268,12 @@ mod tests {
|
||||||
// Load the pair and the swap flag into the circuit.
|
// Load the pair and the swap flag into the circuit.
|
||||||
let x = chip.load_private(layouter.namespace(|| "x"), config.x, self.x)?;
|
let x = chip.load_private(layouter.namespace(|| "x"), config.x, self.x)?;
|
||||||
let y = chip.load_private(layouter.namespace(|| "y"), config.y, self.y)?;
|
let y = chip.load_private(layouter.namespace(|| "y"), config.y, self.y)?;
|
||||||
let swap =
|
|
||||||
chip.load_private(layouter.namespace(|| "swap"), config.swap, self.swap)?;
|
|
||||||
|
|
||||||
// Return the swapped pair.
|
// Return the swapped pair.
|
||||||
let swapped_pair =
|
let swapped_pair =
|
||||||
chip.swap(layouter.namespace(|| "swap"), (x, y).into(), swap.into())?;
|
chip.swap(layouter.namespace(|| "swap"), (x, y).into(), self.swap)?;
|
||||||
|
|
||||||
if let Some(swap) = self.swap {
|
if let Some(swap) = self.swap {
|
||||||
if swap == F::one() {
|
if swap {
|
||||||
// Check that `x` and `y` have been swapped
|
// Check that `x` and `y` have been swapped
|
||||||
assert_eq!(swapped_pair.0.value.unwrap(), y.value.unwrap());
|
assert_eq!(swapped_pair.0.value.unwrap(), y.value.unwrap());
|
||||||
assert_eq!(swapped_pair.1.value.unwrap(), x.value.unwrap());
|
assert_eq!(swapped_pair.1.value.unwrap(), x.value.unwrap());
|
||||||
|
@ -330,7 +293,7 @@ mod tests {
|
||||||
let circuit: MyCircuit<Base> = MyCircuit {
|
let circuit: MyCircuit<Base> = MyCircuit {
|
||||||
x: Some(Base::rand()),
|
x: Some(Base::rand()),
|
||||||
y: Some(Base::rand()),
|
y: Some(Base::rand()),
|
||||||
swap: Some(Base::one()),
|
swap: Some(true),
|
||||||
};
|
};
|
||||||
let prover = match MockProver::<Base>::run(3, &circuit, vec![]) {
|
let prover = match MockProver::<Base>::run(3, &circuit, vec![]) {
|
||||||
Ok(prover) => prover,
|
Ok(prover) => prover,
|
||||||
|
@ -344,7 +307,7 @@ mod tests {
|
||||||
let circuit: MyCircuit<Base> = MyCircuit {
|
let circuit: MyCircuit<Base> = MyCircuit {
|
||||||
x: Some(Base::rand()),
|
x: Some(Base::rand()),
|
||||||
y: Some(Base::rand()),
|
y: Some(Base::rand()),
|
||||||
swap: Some(Base::zero()),
|
swap: Some(false),
|
||||||
};
|
};
|
||||||
let prover = match MockProver::<Base>::run(3, &circuit, vec![]) {
|
let prover = match MockProver::<Base>::run(3, &circuit, vec![]) {
|
||||||
Ok(prover) => prover,
|
Ok(prover) => prover,
|
||||||
|
|
Loading…
Reference in New Issue