utilities::cond_swap: Directly witness swap bit

This commit is contained in:
therealyingtong 2021-06-05 09:43:02 +08:00
parent f31b9feba0
commit 6603e996ed
1 changed files with 31 additions and 68 deletions

View File

@ -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,