mirror of https://github.com/zcash/orchard.git
232 lines
6.9 KiB
Rust
232 lines
6.9 KiB
Rust
use super::{copy, CellValue, UtilitiesInstructions};
|
|
use halo2::{
|
|
circuit::{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> {
|
|
/// Enforces that `value` be zero or, if non-zero, that `enable_flag` must be 1.
|
|
fn enable_flag(
|
|
&self,
|
|
layouter: impl Layouter<F>,
|
|
value: Self::Var,
|
|
enable_flag: Option<bool>,
|
|
) -> 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 {
|
|
&()
|
|
}
|
|
}
|
|
|
|
impl<F: FieldExt> UtilitiesInstructions<F> for EnableFlagChip<F> {
|
|
type Var = CellValue<F>;
|
|
}
|
|
|
|
impl<F: FieldExt> EnableFlagInstructions<F> for EnableFlagChip<F> {
|
|
fn enable_flag(
|
|
&self,
|
|
mut layouter: impl Layouter<F>,
|
|
value: Self::Var,
|
|
enable_flag: Option<bool>,
|
|
) -> 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)?;
|
|
|
|
// Witness `enable_flag` value
|
|
let enable_flag_val = enable_flag.map(|flag| F::from_u64(flag as u64));
|
|
region.assign_advice(
|
|
|| "enable_flag",
|
|
config.enable_flag,
|
|
0,
|
|
|| enable_flag_val.ok_or(Error::SynthesisError),
|
|
)?;
|
|
|
|
// 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.
|
|
///
|
|
/// `perm` must cover `advices[0]`, as well as any columns that will be
|
|
/// passed to this chip.
|
|
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);
|
|
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<bool>,
|
|
}
|
|
|
|
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)?;
|
|
|
|
// Run the enable flag logic.
|
|
chip.enable_flag(layouter.namespace(|| "swap"), value, self.enable_flag)?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
// Test value = 1, flag = 1 case (success)
|
|
{
|
|
let circuit: MyCircuit<Base> = MyCircuit {
|
|
value: Some(Base::one()),
|
|
enable_flag: Some(true),
|
|
};
|
|
let prover = MockProver::<Base>::run(1, &circuit, vec![]).unwrap();
|
|
assert_eq!(prover.verify(), Ok(()));
|
|
}
|
|
|
|
// Test value = 0, flag = 0 case (success)
|
|
{
|
|
let circuit: MyCircuit<Base> = MyCircuit {
|
|
value: Some(Base::zero()),
|
|
enable_flag: Some(false),
|
|
};
|
|
let prover = MockProver::<Base>::run(1, &circuit, vec![]).unwrap();
|
|
assert_eq!(prover.verify(), Ok(()));
|
|
}
|
|
|
|
// Test value = 0, flag = 1 case (success)
|
|
{
|
|
let circuit: MyCircuit<Base> = MyCircuit {
|
|
value: Some(Base::zero()),
|
|
enable_flag: Some(true),
|
|
};
|
|
let prover = MockProver::<Base>::run(1, &circuit, vec![]).unwrap();
|
|
assert_eq!(prover.verify(), Ok(()));
|
|
}
|
|
|
|
// Test value = 1, flag = 0 case (error)
|
|
{
|
|
let circuit: MyCircuit<Base> = MyCircuit {
|
|
value: Some(Base::one()),
|
|
enable_flag: Some(false),
|
|
};
|
|
let prover = MockProver::<Base>::run(1, &circuit, vec![]).unwrap();
|
|
assert_eq!(
|
|
prover.verify(),
|
|
Err(vec![VerifyFailure::Constraint {
|
|
gate_index: 0,
|
|
gate_name: "Enable flag",
|
|
constraint_index: 0,
|
|
constraint_name: "",
|
|
row: 1,
|
|
}])
|
|
);
|
|
}
|
|
}
|
|
}
|