mirror of https://github.com/zcash/halo2.git
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:
parent
6f4b5b0340
commit
a10aefc8c1
|
@ -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)
|
||||
},
|
||||
)?;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
}])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue