mirror of https://github.com/zcash/halo2.git
commit
2be13bfa80
|
@ -1,2 +1,3 @@
|
|||
pub(crate) mod ecc;
|
||||
pub(crate) mod poseidon;
|
||||
pub(crate) mod utilities;
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
use halo2::{
|
||||
circuit::{Cell, Chip, Layouter, Region},
|
||||
plonk::{Advice, Column, Error, Permutation},
|
||||
};
|
||||
use pasta_curves::arithmetic::FieldExt;
|
||||
|
||||
mod cond_swap;
|
||||
mod enable_flag;
|
||||
mod plonk;
|
||||
|
||||
/// A variable representing a number.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct CellValue<F: FieldExt> {
|
||||
cell: Cell,
|
||||
value: Option<F>,
|
||||
}
|
||||
|
||||
pub trait Var<F: FieldExt> {
|
||||
fn new(cell: Cell, value: Option<F>) -> Self;
|
||||
fn cell(&self) -> Cell;
|
||||
fn value(&self) -> Option<F>;
|
||||
}
|
||||
|
||||
impl<F: FieldExt> Var<F> for CellValue<F> {
|
||||
fn new(cell: Cell, value: Option<F>) -> Self {
|
||||
Self { cell, value }
|
||||
}
|
||||
|
||||
fn cell(&self) -> Cell {
|
||||
self.cell
|
||||
}
|
||||
|
||||
fn value(&self) -> Option<F> {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
pub trait UtilitiesInstructions<F: FieldExt>: Chip<F> {
|
||||
type Var: Var<F>;
|
||||
|
||||
fn load_private(
|
||||
&self,
|
||||
mut layouter: impl Layouter<F>,
|
||||
column: Column<Advice>,
|
||||
value: Option<F>,
|
||||
) -> Result<Self::Var, Error> {
|
||||
layouter.assign_region(
|
||||
|| "load private",
|
||||
|mut region| {
|
||||
let cell = region.assign_advice(
|
||||
|| "load private",
|
||||
column,
|
||||
0,
|
||||
|| value.ok_or(Error::SynthesisError),
|
||||
)?;
|
||||
Ok(Var::new(cell, value))
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Assigns a cell at a specific offset within the given region, constraining it
|
||||
/// to the same value as another cell (which may be in any region).
|
||||
///
|
||||
/// Returns an error if either `column` or `copy` is not within `perm`.
|
||||
pub fn copy<A, AR, F: FieldExt>(
|
||||
region: &mut Region<'_, F>,
|
||||
annotation: A,
|
||||
column: Column<Advice>,
|
||||
offset: usize,
|
||||
copy: &CellValue<F>,
|
||||
perm: &Permutation,
|
||||
) -> Result<CellValue<F>, Error>
|
||||
where
|
||||
A: Fn() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
let cell = region.assign_advice(annotation, column, offset, || {
|
||||
copy.value.ok_or(Error::SynthesisError)
|
||||
})?;
|
||||
|
||||
region.constrain_equal(perm, cell, copy.cell)?;
|
||||
|
||||
Ok(CellValue::new(cell, copy.value))
|
||||
}
|
|
@ -0,0 +1,291 @@
|
|||
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::{array, marker::PhantomData};
|
||||
|
||||
pub trait CondSwapInstructions<F: FieldExt>: UtilitiesInstructions<F> {
|
||||
#[allow(clippy::type_complexity)]
|
||||
/// Given an input pair (a,b) and a `swap` boolean flag, returns
|
||||
/// (b,a) if `swap` is set, else (a,b) if `swap` is not set.
|
||||
fn swap(
|
||||
&self,
|
||||
layouter: impl Layouter<F>,
|
||||
pair: (Self::Var, Self::Var),
|
||||
swap: Option<bool>,
|
||||
) -> Result<(Self::Var, Self::Var), Error>;
|
||||
}
|
||||
|
||||
/// A chip implementing a conditional swap.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CondSwapChip<F> {
|
||||
config: CondSwapConfig,
|
||||
_marker: PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<F: FieldExt> Chip<F> for CondSwapChip<F> {
|
||||
type Config = CondSwapConfig;
|
||||
type Loaded = ();
|
||||
|
||||
fn config(&self) -> &Self::Config {
|
||||
&self.config
|
||||
}
|
||||
|
||||
fn loaded(&self) -> &Self::Loaded {
|
||||
&()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CondSwapConfig {
|
||||
pub q_swap: Selector,
|
||||
pub a: Column<Advice>,
|
||||
pub b: Column<Advice>,
|
||||
pub a_swapped: Column<Advice>,
|
||||
pub b_swapped: Column<Advice>,
|
||||
pub swap: Column<Advice>,
|
||||
pub perm: Permutation,
|
||||
}
|
||||
|
||||
impl<F: FieldExt> UtilitiesInstructions<F> for CondSwapChip<F> {
|
||||
type Var = CellValue<F>;
|
||||
}
|
||||
|
||||
impl<F: FieldExt> CondSwapInstructions<F> for CondSwapChip<F> {
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn swap(
|
||||
&self,
|
||||
mut layouter: impl Layouter<F>,
|
||||
pair: (Self::Var, Self::Var),
|
||||
swap: Option<bool>,
|
||||
) -> Result<(Self::Var, Self::Var), Error> {
|
||||
let config = self.config();
|
||||
|
||||
layouter.assign_region(
|
||||
|| "swap",
|
||||
|mut region| {
|
||||
// Enable `q_swap` selector
|
||||
config.q_swap.enable(&mut region, 0)?;
|
||||
|
||||
// Copy in `a` value
|
||||
let a = copy(&mut region, || "copy a", config.a, 0, &pair.0, &config.perm)?;
|
||||
|
||||
// Copy in `b` value
|
||||
let b = copy(&mut region, || "copy b", config.b, 0, &pair.1, &config.perm)?;
|
||||
|
||||
// Witness `swap` value
|
||||
let swap_val = swap.map(|swap| F::from_u64(swap as u64));
|
||||
region.assign_advice(
|
||||
|| "swap",
|
||||
config.swap,
|
||||
0,
|
||||
|| swap_val.ok_or(Error::SynthesisError),
|
||||
)?;
|
||||
|
||||
// Conditionally swap a
|
||||
let a_swapped = {
|
||||
let a_swapped = a
|
||||
.value
|
||||
.zip(b.value)
|
||||
.zip(swap)
|
||||
.map(|((a, b), swap)| if swap { b } else { a });
|
||||
let a_swapped_cell = region.assign_advice(
|
||||
|| "a_swapped",
|
||||
config.a_swapped,
|
||||
0,
|
||||
|| a_swapped.ok_or(Error::SynthesisError),
|
||||
)?;
|
||||
CellValue {
|
||||
cell: a_swapped_cell,
|
||||
value: a_swapped,
|
||||
}
|
||||
};
|
||||
|
||||
// Conditionally swap b
|
||||
let b_swapped = {
|
||||
let b_swapped = a
|
||||
.value
|
||||
.zip(b.value)
|
||||
.zip(swap)
|
||||
.map(|((a, b), swap)| if swap { a } else { b });
|
||||
let b_swapped_cell = region.assign_advice(
|
||||
|| "b_swapped",
|
||||
config.b_swapped,
|
||||
0,
|
||||
|| b_swapped.ok_or(Error::SynthesisError),
|
||||
)?;
|
||||
CellValue {
|
||||
cell: b_swapped_cell,
|
||||
value: b_swapped,
|
||||
}
|
||||
};
|
||||
|
||||
// Return swapped pair
|
||||
Ok((a_swapped, b_swapped))
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FieldExt> CondSwapChip<F> {
|
||||
/// Configures this chip for use in a circuit.
|
||||
///
|
||||
/// `perm` must cover `advices[0..2]`, as well as any columns that will
|
||||
/// be passed to this chip.
|
||||
pub fn configure(
|
||||
meta: &mut ConstraintSystem<F>,
|
||||
advices: [Column<Advice>; 5],
|
||||
perm: Permutation,
|
||||
) -> CondSwapConfig {
|
||||
let q_swap = meta.selector();
|
||||
|
||||
let config = CondSwapConfig {
|
||||
q_swap,
|
||||
a: advices[0],
|
||||
b: advices[1],
|
||||
a_swapped: advices[2],
|
||||
b_swapped: advices[3],
|
||||
swap: advices[4],
|
||||
perm,
|
||||
};
|
||||
|
||||
// TODO: optimise shape of gate for Merkle path validation
|
||||
|
||||
meta.create_gate("a' = b ⋅ swap + a ⋅ (1-swap)", |meta| {
|
||||
let q_swap = meta.query_selector(q_swap, Rotation::cur());
|
||||
|
||||
let a = meta.query_advice(config.a, Rotation::cur());
|
||||
let b = meta.query_advice(config.b, Rotation::cur());
|
||||
let a_swapped = meta.query_advice(config.a_swapped, Rotation::cur());
|
||||
let b_swapped = meta.query_advice(config.b_swapped, Rotation::cur());
|
||||
let swap = meta.query_advice(config.swap, Rotation::cur());
|
||||
|
||||
let one = Expression::Constant(F::one());
|
||||
|
||||
// a_swapped - b ⋅ swap - a ⋅ (1-swap) = 0
|
||||
// This checks that `a_swapped` is equal to `y` when `swap` is set,
|
||||
// but remains as `a` when `swap` is not set.
|
||||
let a_check =
|
||||
a_swapped - b.clone() * swap.clone() - a.clone() * (one.clone() - swap.clone());
|
||||
|
||||
// b_swapped - a ⋅ swap - b ⋅ (1-swap) = 0
|
||||
// This checks that `b_swapped` is equal to `a` when `swap` is set,
|
||||
// but remains as `b` when `swap` is not set.
|
||||
let b_check = b_swapped - a * swap.clone() - b * (one.clone() - swap.clone());
|
||||
|
||||
// Check `swap` is boolean.
|
||||
let bool_check = swap.clone() * (one - swap);
|
||||
|
||||
array::IntoIter::new([a_check, b_check, bool_check])
|
||||
.map(|poly| q_swap.clone() * poly)
|
||||
.collect()
|
||||
});
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
pub fn construct(config: CondSwapConfig) -> Self {
|
||||
CondSwapChip {
|
||||
config,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::UtilitiesInstructions;
|
||||
use super::{CondSwapChip, CondSwapConfig, CondSwapInstructions};
|
||||
use halo2::{
|
||||
circuit::{layouter::SingleChipLayouter, Layouter},
|
||||
dev::MockProver,
|
||||
plonk::{Any, Assignment, Circuit, Column, ConstraintSystem, Error},
|
||||
};
|
||||
use pasta_curves::{arithmetic::FieldExt, pallas::Base};
|
||||
|
||||
#[test]
|
||||
fn cond_swap() {
|
||||
struct MyCircuit<F: FieldExt> {
|
||||
a: Option<F>,
|
||||
b: Option<F>,
|
||||
swap: Option<bool>,
|
||||
}
|
||||
|
||||
impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
|
||||
type Config = CondSwapConfig;
|
||||
|
||||
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
|
||||
let advices = [
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
];
|
||||
|
||||
let perm = meta.permutation(
|
||||
&advices
|
||||
.iter()
|
||||
.map(|advice| (*advice).into())
|
||||
.collect::<Vec<Column<Any>>>(),
|
||||
);
|
||||
|
||||
CondSwapChip::<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 = CondSwapChip::<F>::construct(config.clone());
|
||||
|
||||
// Load the pair and the swap flag into the circuit.
|
||||
let a = chip.load_private(layouter.namespace(|| "a"), config.a, self.a)?;
|
||||
let b = chip.load_private(layouter.namespace(|| "b"), config.b, self.b)?;
|
||||
// Return the swapped pair.
|
||||
let swapped_pair = chip.swap(layouter.namespace(|| "swap"), (a, b), self.swap)?;
|
||||
|
||||
if let Some(swap) = self.swap {
|
||||
if swap {
|
||||
// Check that `a` and `b` have been swapped
|
||||
assert_eq!(swapped_pair.0.value.unwrap(), b.value.unwrap());
|
||||
assert_eq!(swapped_pair.1.value.unwrap(), a.value.unwrap());
|
||||
} else {
|
||||
// Check that `a` and `b` have not been swapped
|
||||
assert_eq!(swapped_pair.0.value.unwrap(), a.value.unwrap());
|
||||
assert_eq!(swapped_pair.1.value.unwrap(), b.value.unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Test swap case
|
||||
{
|
||||
let circuit: MyCircuit<Base> = MyCircuit {
|
||||
a: Some(Base::rand()),
|
||||
b: Some(Base::rand()),
|
||||
swap: Some(true),
|
||||
};
|
||||
let prover = MockProver::<Base>::run(3, &circuit, vec![]).unwrap();
|
||||
assert_eq!(prover.verify(), Ok(()));
|
||||
}
|
||||
|
||||
// Test non-swap case
|
||||
{
|
||||
let circuit: MyCircuit<Base> = MyCircuit {
|
||||
a: Some(Base::rand()),
|
||||
b: Some(Base::rand()),
|
||||
swap: Some(false),
|
||||
};
|
||||
let prover = MockProver::<Base>::run(3, &circuit, vec![]).unwrap();
|
||||
assert_eq!(prover.verify(), Ok(()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
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, 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<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::Gate {
|
||||
gate_index: 0,
|
||||
gate_name: "Enable flag",
|
||||
row: 1,
|
||||
}])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,344 @@
|
|||
use super::{copy, CellValue, UtilitiesInstructions};
|
||||
use halo2::{
|
||||
circuit::{Chip, Layouter},
|
||||
plonk::{Advice, Column, ConstraintSystem, Error, Fixed, Permutation},
|
||||
poly::Rotation,
|
||||
};
|
||||
use pasta_curves::arithmetic::FieldExt;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub trait PLONKInstructions<F: FieldExt>: UtilitiesInstructions<F> {
|
||||
// Checks that a * sm * b = c * sc
|
||||
fn mul(
|
||||
&self,
|
||||
layouter: impl Layouter<F>,
|
||||
a: Self::Var,
|
||||
b: Self::Var,
|
||||
c: Self::Var,
|
||||
sc: Option<F>,
|
||||
sm: Option<F>,
|
||||
) -> Result<(), Error>;
|
||||
// Checks that a * sa + b * sb = c * sc
|
||||
fn add(
|
||||
&self,
|
||||
layouter: impl Layouter<F>,
|
||||
a: Self::Var,
|
||||
b: Self::Var,
|
||||
c: Self::Var,
|
||||
sa: Option<F>,
|
||||
sb: Option<F>,
|
||||
sc: Option<F>,
|
||||
) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PLONKConfig {
|
||||
a: Column<Advice>,
|
||||
b: Column<Advice>,
|
||||
c: Column<Advice>,
|
||||
|
||||
sa: Column<Fixed>,
|
||||
sb: Column<Fixed>,
|
||||
sc: Column<Fixed>,
|
||||
sm: Column<Fixed>,
|
||||
|
||||
perm: Permutation,
|
||||
}
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub struct PLONKChip<F: FieldExt> {
|
||||
config: PLONKConfig,
|
||||
_marker: PhantomData<F>,
|
||||
}
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
impl<F: FieldExt> Chip<F> for PLONKChip<F> {
|
||||
type Config = PLONKConfig;
|
||||
type Loaded = ();
|
||||
|
||||
fn config(&self) -> &Self::Config {
|
||||
&self.config
|
||||
}
|
||||
|
||||
fn loaded(&self) -> &Self::Loaded {
|
||||
&()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
impl<F: FieldExt> UtilitiesInstructions<F> for PLONKChip<F> {
|
||||
type Var = CellValue<F>;
|
||||
}
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
impl<F: FieldExt> PLONKInstructions<F> for PLONKChip<F> {
|
||||
fn mul(
|
||||
&self,
|
||||
mut layouter: impl Layouter<F>,
|
||||
a: Self::Var,
|
||||
b: Self::Var,
|
||||
c: Self::Var,
|
||||
sc: Option<F>,
|
||||
sm: Option<F>,
|
||||
) -> Result<(), Error> {
|
||||
layouter.assign_region(
|
||||
|| "mul",
|
||||
|mut region| {
|
||||
let config = self.config().clone();
|
||||
|
||||
// Copy in `a`
|
||||
copy(&mut region, || "copy a", config.a, 0, &a, &config.perm)?;
|
||||
|
||||
// Copy in `b`
|
||||
copy(&mut region, || "copy b", config.b, 0, &b, &config.perm)?;
|
||||
|
||||
// Copy in `c`
|
||||
copy(&mut region, || "copy c", config.c, 0, &c, &config.perm)?;
|
||||
|
||||
// Assign fixed columns
|
||||
region.assign_fixed(|| "sc", config.sc, 0, || sc.ok_or(Error::SynthesisError))?;
|
||||
region.assign_fixed(
|
||||
|| "a * (sm) * b",
|
||||
config.sm,
|
||||
0,
|
||||
|| sm.ok_or(Error::SynthesisError),
|
||||
)?;
|
||||
|
||||
#[cfg(test)]
|
||||
// Checks that a * sm * b = c * sc
|
||||
{
|
||||
if let (Some(a), Some(b), Some(c), Some(sm), Some(sc)) =
|
||||
(a.value, b.value, c.value, sm, sc)
|
||||
{
|
||||
assert_eq!(a * sm * b, c * sc);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn add(
|
||||
&self,
|
||||
mut layouter: impl Layouter<F>,
|
||||
a: Self::Var,
|
||||
b: Self::Var,
|
||||
c: Self::Var,
|
||||
sa: Option<F>,
|
||||
sb: Option<F>,
|
||||
sc: Option<F>,
|
||||
) -> Result<(), Error> {
|
||||
let config = self.config().clone();
|
||||
|
||||
layouter.assign_region(
|
||||
|| "add",
|
||||
|mut region| {
|
||||
// Copy in `a`
|
||||
copy(&mut region, || "copy a", config.a, 0, &a, &config.perm)?;
|
||||
|
||||
// Copy in `b`
|
||||
copy(&mut region, || "copy b", config.b, 0, &b, &config.perm)?;
|
||||
|
||||
// Copy in `c`
|
||||
copy(&mut region, || "copy c", config.c, 0, &c, &config.perm)?;
|
||||
|
||||
// Assign fixed columns
|
||||
region.assign_fixed(|| "a", config.sa, 0, || sa.ok_or(Error::SynthesisError))?;
|
||||
region.assign_fixed(|| "b", config.sb, 0, || sb.ok_or(Error::SynthesisError))?;
|
||||
region.assign_fixed(|| "c", config.sc, 0, || sc.ok_or(Error::SynthesisError))?;
|
||||
|
||||
#[cfg(test)]
|
||||
// Checks that a * sa + b * sb = c * sc
|
||||
{
|
||||
if let (Some(a), Some(b), Some(c), Some(sa), Some(sb), Some(sc)) =
|
||||
(a.value, b.value, c.value, sa, sb, sc)
|
||||
{
|
||||
assert_eq!(a * sa + b * sb, c * sc);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
impl<F: FieldExt> PLONKChip<F> {
|
||||
/// Configures this chip for use in a circuit.
|
||||
///
|
||||
/// `perm` must cover `advices`, as well as any columns that will be passed
|
||||
/// to this chip.
|
||||
pub fn configure(
|
||||
meta: &mut ConstraintSystem<F>,
|
||||
advices: [Column<Advice>; 3],
|
||||
perm: Permutation,
|
||||
) -> PLONKConfig {
|
||||
let a = advices[0];
|
||||
let b = advices[1];
|
||||
let c = advices[2];
|
||||
|
||||
let sa = meta.fixed_column();
|
||||
let sb = meta.fixed_column();
|
||||
let sc = meta.fixed_column();
|
||||
let sm = meta.fixed_column();
|
||||
|
||||
meta.create_gate("Combined add-mult", |meta| {
|
||||
let a = meta.query_advice(a, Rotation::cur());
|
||||
let b = meta.query_advice(b, Rotation::cur());
|
||||
let c = meta.query_advice(c, Rotation::cur());
|
||||
|
||||
let sa = meta.query_fixed(sa, Rotation::cur());
|
||||
let sb = meta.query_fixed(sb, Rotation::cur());
|
||||
let sc = meta.query_fixed(sc, Rotation::cur());
|
||||
let sm = meta.query_fixed(sm, Rotation::cur());
|
||||
|
||||
vec![a.clone() * sa + b.clone() * sb + a * b * sm + (c * sc * (-F::one()))]
|
||||
});
|
||||
|
||||
PLONKConfig {
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
sa,
|
||||
sb,
|
||||
sc,
|
||||
sm,
|
||||
perm,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn construct(config: PLONKConfig) -> Self {
|
||||
PLONKChip {
|
||||
config,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::UtilitiesInstructions;
|
||||
use super::{PLONKChip, PLONKConfig, PLONKInstructions};
|
||||
use halo2::{
|
||||
circuit::{layouter::SingleChipLayouter, Layouter},
|
||||
dev::MockProver,
|
||||
plonk::{Any, Assignment, Circuit, Column, ConstraintSystem, Error},
|
||||
};
|
||||
use pasta_curves::{arithmetic::FieldExt, pallas::Base};
|
||||
|
||||
#[test]
|
||||
fn plonk_util() {
|
||||
struct MyCircuit<F: FieldExt> {
|
||||
a: Option<F>,
|
||||
b: Option<F>,
|
||||
}
|
||||
|
||||
impl<F: FieldExt> Circuit<F> for MyCircuit<F> {
|
||||
type Config = PLONKConfig;
|
||||
|
||||
fn configure(meta: &mut ConstraintSystem<F>) -> Self::Config {
|
||||
let advices = [
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
meta.advice_column(),
|
||||
];
|
||||
|
||||
let perm = meta.permutation(
|
||||
&advices
|
||||
.iter()
|
||||
.map(|advice| (*advice).into())
|
||||
.collect::<Vec<Column<Any>>>(),
|
||||
);
|
||||
|
||||
PLONKChip::<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 = PLONKChip::<F>::construct(config.clone());
|
||||
|
||||
let a = chip.load_private(layouter.namespace(|| "a"), config.a, self.a)?;
|
||||
let b = chip.load_private(layouter.namespace(|| "b"), config.b, self.b)?;
|
||||
|
||||
// a + b = c
|
||||
{
|
||||
let c = self.a.zip(self.b).map(|(a, b)| a + b);
|
||||
let c = chip.load_private(layouter.namespace(|| "c"), config.c, c)?;
|
||||
chip.add(
|
||||
layouter.namespace(|| "a + b = c"),
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
Some(F::one()),
|
||||
Some(F::one()),
|
||||
Some(F::one()),
|
||||
)?;
|
||||
}
|
||||
|
||||
// a * b = c
|
||||
{
|
||||
let c = self.a.zip(self.b).map(|(a, b)| a * b);
|
||||
let c = chip.load_private(layouter.namespace(|| "c"), config.c, c)?;
|
||||
chip.mul(
|
||||
layouter.namespace(|| "a * b = c"),
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
Some(F::one()),
|
||||
Some(F::one()),
|
||||
)?;
|
||||
}
|
||||
|
||||
// 2a + 3b = c
|
||||
{
|
||||
let c = self
|
||||
.a
|
||||
.zip(self.b)
|
||||
.map(|(a, b)| a * F::from_u64(2) + b * F::from_u64(3));
|
||||
let c = chip.load_private(layouter.namespace(|| "c"), config.c, c)?;
|
||||
chip.add(
|
||||
layouter.namespace(|| "2a + 3b = c"),
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
Some(F::from_u64(2)),
|
||||
Some(F::from_u64(3)),
|
||||
Some(F::one()),
|
||||
)?;
|
||||
}
|
||||
|
||||
// 4 * a * b = 2 * c => c = 2ab
|
||||
{
|
||||
let c = self.a.zip(self.b).map(|(a, b)| a * b * F::from_u64(2));
|
||||
let c = chip.load_private(layouter.namespace(|| "c"), config.c, c)?;
|
||||
chip.mul(
|
||||
layouter.namespace(|| "4 * a * b = 2 * c"),
|
||||
a,
|
||||
b,
|
||||
c,
|
||||
Some(F::from_u64(2)),
|
||||
Some(F::from_u64(4)),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let circuit: MyCircuit<Base> = MyCircuit {
|
||||
a: Some(Base::rand()),
|
||||
b: Some(Base::rand()),
|
||||
};
|
||||
let prover = MockProver::<Base>::run(3, &circuit, vec![]).unwrap();
|
||||
assert_eq!(prover.verify(), Ok(()));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue