Skip to content

Commit

Permalink
Add enable flag chip
Browse files Browse the repository at this point in the history
  • Loading branch information
therealyingtong committed Jun 4, 2021
1 parent 4b0ea0b commit fea88c8
Show file tree
Hide file tree
Showing 2 changed files with 286 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/circuit/gadget/utilities.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use halo2::{
use pasta_curves::arithmetic::FieldExt;

mod cond_swap;
mod enable_flag;
mod plonk;

/// A variable representing a number.
Expand Down
285 changes: 285 additions & 0 deletions src/circuit/gadget/utilities/enable_flag.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
use super::{copy, CellValue, UtilitiesInstructions, Var};
use halo2::{
circuit::{Cell, Chip, Layouter},
plonk::{Advice, Column, ConstraintSystem, Error, Expression, Permutation, Selector},
poly::Rotation,
};
use pasta_curves::arithmetic::FieldExt;
use std::marker::PhantomData;

pub trait EnableFlagInstructions<F: FieldExt>: UtilitiesInstructions<F> {
/// Variable representing cell with a certain value in the circuit.
type Var: Var<F>;

/// Variable representing an `enable` boolean flag.
type Flag: From<<Self as EnableFlagInstructions<F>>::Var>;

/// Given a `value` and an `enable_flag`, check that either `value = 0`
/// or `enable_flag = 1`.
fn enable_flag(
&self,
layouter: impl Layouter<F>,
value: <Self as EnableFlagInstructions<F>>::Var,
enable_flag: <Self as EnableFlagInstructions<F>>::Flag,
) -> Result<(), Error>;
}

#[derive(Clone, Debug)]
pub struct EnableFlagConfig {
q_enable: Selector,
value: Column<Advice>,
enable_flag: Column<Advice>,
perm: Permutation,
}

/// A chip implementing an enable flag.
#[derive(Clone, Debug)]
pub struct EnableFlagChip<F> {
config: EnableFlagConfig,
_marker: PhantomData<F>,
}

impl<F: FieldExt> Chip<F> for EnableFlagChip<F> {
type Config = EnableFlagConfig;
type Loaded = ();

fn config(&self) -> &Self::Config {
&self.config
}

fn loaded(&self) -> &Self::Loaded {
&()
}
}

/// A variable representing an `enable` boolean flag.
#[derive(Copy, Clone)]
pub struct Flag {
cell: Cell,
value: Option<bool>,
}

impl<F: FieldExt> From<CellValue<F>> for Flag {
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.")
}
});
Flag {
cell: var.cell,
value,
}
}
}

impl<F: FieldExt> UtilitiesInstructions<F> for EnableFlagChip<F> {
type Var = CellValue<F>;
}

impl<F: FieldExt> EnableFlagInstructions<F> for EnableFlagChip<F> {
type Var = CellValue<F>;
type Flag = Flag;

fn enable_flag(
&self,
mut layouter: impl Layouter<F>,
value: <Self as EnableFlagInstructions<F>>::Var,
enable_flag: <Self as EnableFlagInstructions<F>>::Flag,
) -> Result<(), Error> {
let config = self.config().clone();
layouter.assign_region(
|| "enable flag",
|mut region| {
// Enable `q_enable` selector
config.q_enable.enable(&mut region, 0)?;

// Copy in `enable_flag` value
let enable_flag_val = enable_flag.value;
let enable_flag_cell = region.assign_advice(
|| "enable_flag",
config.enable_flag,
0,
|| {
enable_flag_val
.map(|enable_flag| F::from_u64(enable_flag as u64))
.ok_or(Error::SynthesisError)
},
)?;
region.constrain_equal(&config.perm, enable_flag_cell, enable_flag.cell)?;

// Copy `value`
copy(
&mut region,
|| "copy value",
config.value,
0,
&value,
&config.perm,
)?;

Ok(())
},
)
}
}

impl<F: FieldExt> EnableFlagChip<F> {
/// Configures this chip for use in a circuit.
pub fn configure(
meta: &mut ConstraintSystem<F>,
advices: [Column<Advice>; 2],
perm: Permutation,
) -> EnableFlagConfig {
let q_enable = meta.selector();

let config = EnableFlagConfig {
q_enable,
value: advices[0],
enable_flag: advices[1],
perm,
};

meta.create_gate("Enable flag", |meta| {
let q_enable = meta.query_selector(config.q_enable, Rotation::cur());
let value = meta.query_advice(config.value, Rotation::cur());
let enable_flag = meta.query_advice(config.enable_flag, Rotation::cur());

vec![q_enable * (Expression::Constant(F::one()) - enable_flag) * value]
});

config
}

pub fn construct(config: EnableFlagConfig) -> Self {
EnableFlagChip {
config,
_marker: PhantomData,
}
}
}

#[cfg(test)]
mod tests {
use super::super::UtilitiesInstructions;
use super::{EnableFlagChip, EnableFlagConfig, EnableFlagInstructions};
use halo2::{
circuit::{layouter::SingleChipLayouter, Layouter},
dev::{MockProver, VerifyFailure},
plonk::{Any, Assignment, Circuit, Column, ConstraintSystem, Error},
};
use pasta_curves::{arithmetic::FieldExt, pallas::Base};

#[test]
fn enable_flag() {
struct MyCircuit<F: FieldExt> {
value: Option<F>,
enable_flag: Option<F>,
}

impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
type Config = EnableFlagConfig;

fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
let advices = [meta.advice_column(), meta.advice_column()];

let perm = meta.permutation(
&advices
.iter()
.map(|advice| (*advice).into())
.collect::<Vec<Column<Any>>>(),
);

EnableFlagChip::<F>::configure(meta, advices, perm)
}

fn synthesize(
&self,
cs: &mut impl Assignment<F>,
config: Self::Config,
) -> Result<(), Error> {
let mut layouter = SingleChipLayouter::new(cs)?;
let chip = EnableFlagChip::<F>::construct(config.clone());

// Load the value and the enable flag into the circuit.
let value =
chip.load_private(layouter.namespace(|| "value"), config.value, self.value)?;
let enable_flag = chip.load_private(
layouter.namespace(|| "enable_flag"),
config.enable_flag,
self.enable_flag,
)?;

// Run the enable flag logic.
chip.enable_flag(layouter.namespace(|| "swap"), value, enable_flag.into())?;

Ok(())
}
}

// Test value = 1, flag = 1 case (success)
{
let circuit: MyCircuit<Base> = MyCircuit {
value: Some(Base::one()),
enable_flag: Some(Base::one()),
};
let prover = match MockProver::<Base>::run(1, &circuit, vec![]) {
Ok(prover) => prover,
Err(e) => panic!("{:?}", e),
};
assert_eq!(prover.verify(), Ok(()));
}

// Test value = 0, flag = 0 case (success)
{
let circuit: MyCircuit<Base> = MyCircuit {
value: Some(Base::zero()),
enable_flag: Some(Base::zero()),
};
let prover = match MockProver::<Base>::run(1, &circuit, vec![]) {
Ok(prover) => prover,
Err(e) => panic!("{:?}", e),
};
assert_eq!(prover.verify(), Ok(()));
}

// Test value = 0, flag = 1 case (success)
{
let circuit: MyCircuit<Base> = MyCircuit {
value: Some(Base::zero()),
enable_flag: Some(Base::one()),
};
let prover = match MockProver::<Base>::run(1, &circuit, vec![]) {
Ok(prover) => prover,
Err(e) => panic!("{:?}", e),
};
assert_eq!(prover.verify(), Ok(()));
}

// Test value = 1, flag = 0 case (error)
{
let circuit: MyCircuit<Base> = MyCircuit {
value: Some(Base::one()),
enable_flag: Some(Base::zero()),
};
let prover = match MockProver::<Base>::run(1, &circuit, vec![]) {
Ok(prover) => prover,
Err(e) => panic!("{:?}", e),
};
assert_eq!(
prover.verify(),
Err(vec![VerifyFailure::Gate {
gate_index: 0,
gate_name: "Enable flag",
row: 1,
}])
);
}
}
}

0 comments on commit fea88c8

Please sign in to comment.