circuit.rs: Implement enable spends and enable outputs logic.

The utilities::enable_flag gadget has been deleted, since the
public inputs API makes it more convenient to inline the gate.
This commit is contained in:
therealyingtong 2021-07-20 01:01:39 +08:00
parent 6f4b5b0340
commit a10aefc8c1
3 changed files with 30 additions and 231 deletions

View File

@ -5,7 +5,7 @@ use std::mem;
use group::{Curve, GroupEncoding};
use halo2::{
circuit::{Layouter, SimpleFloorPlanner},
plonk::{self, Advice, Column, Instance as InstanceColumn, Selector},
plonk::{self, Advice, Column, Expression, Instance as InstanceColumn, Selector},
poly::Rotation,
transcript::{Blake2bRead, Blake2bWrite},
};
@ -55,7 +55,6 @@ use gadget::{
},
utilities::{
copy,
enable_flag::{EnableFlagChip, EnableFlagConfig},
plonk::{PLONKChip, PLONKConfig, PLONKInstructions},
CellValue, UtilitiesInstructions, Var,
},
@ -87,7 +86,6 @@ pub struct Config {
primary: Column<InstanceColumn>,
q_orchard: Selector,
advices: [Column<Advice>; 10],
enable_flag_config: EnableFlagConfig,
ecc_config: EccConfig,
poseidon_config: PoseidonConfig<pallas::Base>,
plonk_config: PLONKConfig,
@ -153,6 +151,8 @@ impl plonk::Circuit<pallas::Base> for Circuit {
// Constrain v_old - v_new = magnitude * sign
// Either v_old = 0, or anchor equals public input
// Constrain v_old = 0 or enable_spends = 1.
// Constrain v_new = 0 or enable_outputs = 1.
let q_orchard = meta.selector();
meta.create_gate("Orchard circuit checks", |meta| {
let q_orchard = meta.query_selector(q_orchard);
@ -164,14 +164,23 @@ impl plonk::Circuit<pallas::Base> for Circuit {
let anchor = meta.query_advice(advices[4], Rotation::cur());
let pub_input_anchor = meta.query_advice(advices[5], Rotation::cur());
let one = Expression::Constant(pallas::Base::one());
let not_enable_spends = one.clone() - meta.query_advice(advices[6], Rotation::cur());
let not_enable_outputs = one - meta.query_advice(advices[7], Rotation::cur());
std::array::IntoIter::new([
(
"v_old - v_new = magnitude * sign",
v_old.clone() - v_new - magnitude * sign,
v_old.clone() - v_new.clone() - magnitude * sign,
),
(
"Either v_old = 0, or anchor equals public input",
v_old * (anchor - pub_input_anchor),
v_old.clone() * (anchor - pub_input_anchor),
),
("v_old = 0 or enable_spends = 1", v_old * not_enable_spends),
(
"v_new = 0 or enable_outputs = 1",
v_new * not_enable_outputs,
),
])
.map(move |(name, poly)| (name, q_orchard.clone() * poly))
@ -221,10 +230,6 @@ impl plonk::Circuit<pallas::Base> for Circuit {
meta.enable_equality((*fixed).into());
}
// Configuration for `enable_spends` and `enable_outputs` flags logic
// TODO: this may change with public inputs API.
let enable_flag_config = EnableFlagChip::configure(meta, [advices[0], advices[1]]);
// Configuration for curve point operations.
// This uses 10 advice columns and spans the whole circuit.
let ecc_config = EccChip::configure(meta, advices, table_idx, ecc_constants);
@ -291,7 +296,6 @@ impl plonk::Circuit<pallas::Base> for Circuit {
primary,
q_orchard,
advices,
enable_flag_config,
ecc_config,
poseidon_config,
plonk_config,
@ -717,6 +721,22 @@ impl plonk::Circuit<pallas::Base> for Circuit {
0,
)?;
region.assign_advice_from_instance(
|| "enable spends",
config.primary,
ENABLE_SPEND,
config.advices[6],
0,
)?;
region.assign_advice_from_instance(
|| "enable outputs",
config.primary,
ENABLE_OUTPUT,
config.advices[7],
0,
)?;
config.q_orchard.enable(&mut region, 0)
},
)?;

View File

@ -8,7 +8,6 @@ use std::{array, convert::TryInto, ops::Range};
pub(crate) mod cond_swap;
pub(crate) mod decompose_running_sum;
pub(crate) mod enable_flag;
pub(crate) mod lookup_range_check;
pub(crate) mod plonk;

View File

@ -1,220 +0,0 @@
use super::{copy, CellValue, UtilitiesInstructions};
use halo2::{
circuit::{Chip, Layouter},
plonk::{Advice, Column, ConstraintSystem, Error, Expression, 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>,
}
/// 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)?;
Ok(())
},
)
}
}
impl<F: FieldExt> EnableFlagChip<F> {
/// Configures this chip for use in a circuit.
///
/// # Side-effects
///
/// `advices[0]` will be equality-enabled.
pub fn configure(
meta: &mut ConstraintSystem<F>,
advices: [Column<Advice>; 2],
) -> EnableFlagConfig {
let value = advices[0];
meta.enable_equality(value.into());
let q_enable = meta.selector();
let config = EnableFlagConfig {
q_enable,
value,
enable_flag: advices[1],
};
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, SimpleFloorPlanner},
dev::{MockProver, VerifyFailure},
plonk::{Circuit, ConstraintSystem, Error},
};
use pasta_curves::{arithmetic::FieldExt, pallas::Base};
#[test]
fn enable_flag() {
#[derive(Default)]
struct MyCircuit<F: FieldExt> {
value: Option<F>,
enable_flag: Option<bool>,
}
impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
type Config = EnableFlagConfig;
type FloorPlanner = SimpleFloorPlanner;
fn without_witnesses(&self) -> Self {
Self::default()
}
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
let advices = [meta.advice_column(), meta.advice_column()];
EnableFlagChip::<F>::configure(meta, advices)
}
fn synthesize(
&self,
config: Self::Config,
mut layouter: impl Layouter<F>,
) -> Result<(), Error> {
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(3, &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(3, &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(3, &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(3, &circuit, vec![]).unwrap();
assert_eq!(
prover.verify(),
Err(vec![VerifyFailure::ConstraintNotSatisfied {
constraint: ((0, "Enable flag").into(), 0, "").into(),
row: 1,
}])
);
}
}
}