mirror of https://github.com/zcash/halo2.git
Documentation improvements and minor refactors.
Co-authored-by: Jack Grigg <jack@electriccoin.co>
This commit is contained in:
parent
0f2dfc5508
commit
54c8cfd1d0
|
@ -59,12 +59,15 @@ pub trait UtilitiesInstructions<F: FieldExt>: Chip<F> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assign a cell the same value as another cell and set up a copy constraint between them.
|
/// 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>(
|
pub fn copy<A, AR, F: FieldExt>(
|
||||||
region: &mut Region<'_, F>,
|
region: &mut Region<'_, F>,
|
||||||
annotation: A,
|
annotation: A,
|
||||||
column: Column<Advice>,
|
column: Column<Advice>,
|
||||||
row: usize,
|
offset: usize,
|
||||||
copy: &CellValue<F>,
|
copy: &CellValue<F>,
|
||||||
perm: &Permutation,
|
perm: &Permutation,
|
||||||
) -> Result<CellValue<F>, Error>
|
) -> Result<CellValue<F>, Error>
|
||||||
|
@ -72,7 +75,7 @@ where
|
||||||
A: Fn() -> AR,
|
A: Fn() -> AR,
|
||||||
AR: Into<String>,
|
AR: Into<String>,
|
||||||
{
|
{
|
||||||
let cell = region.assign_advice(annotation, column, row, || {
|
let cell = region.assign_advice(annotation, column, offset, || {
|
||||||
copy.value.ok_or(Error::SynthesisError)
|
copy.value.ok_or(Error::SynthesisError)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,12 @@ use halo2::{
|
||||||
poly::Rotation,
|
poly::Rotation,
|
||||||
};
|
};
|
||||||
use pasta_curves::arithmetic::FieldExt;
|
use pasta_curves::arithmetic::FieldExt;
|
||||||
use std::marker::PhantomData;
|
use std::{array, marker::PhantomData};
|
||||||
|
|
||||||
pub trait CondSwapInstructions<F: FieldExt>: UtilitiesInstructions<F> {
|
pub trait CondSwapInstructions<F: FieldExt>: UtilitiesInstructions<F> {
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
/// Given an input pair (x,y) and a `swap` boolean flag, return
|
/// Given an input pair (a,b) and a `swap` boolean flag, returns
|
||||||
/// (y,x) if `swap` is set, else (x,y) if `swap` is not set.
|
/// (b,a) if `swap` is set, else (a,b) if `swap` is not set.
|
||||||
fn swap(
|
fn swap(
|
||||||
&self,
|
&self,
|
||||||
layouter: impl Layouter<F>,
|
layouter: impl Layouter<F>,
|
||||||
|
@ -42,10 +42,10 @@ impl<F: FieldExt> Chip<F> for CondSwapChip<F> {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct CondSwapConfig {
|
pub struct CondSwapConfig {
|
||||||
pub q_swap: Selector,
|
pub q_swap: Selector,
|
||||||
pub x: Column<Advice>,
|
pub a: Column<Advice>,
|
||||||
pub y: Column<Advice>,
|
pub b: Column<Advice>,
|
||||||
pub x_swapped: Column<Advice>,
|
pub a_swapped: Column<Advice>,
|
||||||
pub y_swapped: Column<Advice>,
|
pub b_swapped: Column<Advice>,
|
||||||
pub swap: Column<Advice>,
|
pub swap: Column<Advice>,
|
||||||
pub perm: Permutation,
|
pub perm: Permutation,
|
||||||
}
|
}
|
||||||
|
@ -70,11 +70,11 @@ impl<F: FieldExt> CondSwapInstructions<F> for CondSwapChip<F> {
|
||||||
// Enable `q_swap` selector
|
// Enable `q_swap` selector
|
||||||
config.q_swap.enable(&mut region, 0)?;
|
config.q_swap.enable(&mut region, 0)?;
|
||||||
|
|
||||||
// Copy in `x` value
|
// Copy in `a` value
|
||||||
let x = copy(&mut region, || "copy x", config.x, 0, &pair.0, &config.perm)?;
|
let a = copy(&mut region, || "copy a", config.a, 0, &pair.0, &config.perm)?;
|
||||||
|
|
||||||
// Copy in `y` value
|
// Copy in `b` value
|
||||||
let y = copy(&mut region, || "copy y", config.y, 0, &pair.1, &config.perm)?;
|
let b = copy(&mut region, || "copy b", config.b, 0, &pair.1, &config.perm)?;
|
||||||
|
|
||||||
// Witness `swap` value
|
// Witness `swap` value
|
||||||
let swap_val = swap.map(|swap| F::from_u64(swap as u64));
|
let swap_val = swap.map(|swap| F::from_u64(swap as u64));
|
||||||
|
@ -85,46 +85,46 @@ impl<F: FieldExt> CondSwapInstructions<F> for CondSwapChip<F> {
|
||||||
|| swap_val.ok_or(Error::SynthesisError),
|
|| swap_val.ok_or(Error::SynthesisError),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Conditionally swap x
|
// Conditionally swap a
|
||||||
let x_swapped = {
|
let a_swapped = {
|
||||||
let x_swapped = x
|
let a_swapped = a
|
||||||
.value
|
.value
|
||||||
.zip(y.value)
|
.zip(b.value)
|
||||||
.zip(swap)
|
.zip(swap)
|
||||||
.map(|((x, y), swap)| if swap { y } else { x });
|
.map(|((a, b), swap)| if swap { b } else { a });
|
||||||
let x_swapped_cell = region.assign_advice(
|
let a_swapped_cell = region.assign_advice(
|
||||||
|| "x_swapped",
|
|| "a_swapped",
|
||||||
config.x_swapped,
|
config.a_swapped,
|
||||||
0,
|
0,
|
||||||
|| x_swapped.ok_or(Error::SynthesisError),
|
|| a_swapped.ok_or(Error::SynthesisError),
|
||||||
)?;
|
)?;
|
||||||
CellValue {
|
CellValue {
|
||||||
cell: x_swapped_cell,
|
cell: a_swapped_cell,
|
||||||
value: x_swapped,
|
value: a_swapped,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Conditionally swap y
|
// Conditionally swap b
|
||||||
let y_swapped = {
|
let b_swapped = {
|
||||||
let y_swapped = x
|
let b_swapped = a
|
||||||
.value
|
.value
|
||||||
.zip(y.value)
|
.zip(b.value)
|
||||||
.zip(swap)
|
.zip(swap)
|
||||||
.map(|((x, y), swap)| if swap { x } else { y });
|
.map(|((a, b), swap)| if swap { a } else { b });
|
||||||
let y_swapped_cell = region.assign_advice(
|
let b_swapped_cell = region.assign_advice(
|
||||||
|| "y_swapped",
|
|| "b_swapped",
|
||||||
config.y_swapped,
|
config.b_swapped,
|
||||||
0,
|
0,
|
||||||
|| y_swapped.ok_or(Error::SynthesisError),
|
|| b_swapped.ok_or(Error::SynthesisError),
|
||||||
)?;
|
)?;
|
||||||
CellValue {
|
CellValue {
|
||||||
cell: y_swapped_cell,
|
cell: b_swapped_cell,
|
||||||
value: y_swapped,
|
value: b_swapped,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Return swapped pair
|
// Return swapped pair
|
||||||
Ok((x_swapped, y_swapped))
|
Ok((a_swapped, b_swapped))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -132,6 +132,9 @@ impl<F: FieldExt> CondSwapInstructions<F> for CondSwapChip<F> {
|
||||||
|
|
||||||
impl<F: FieldExt> CondSwapChip<F> {
|
impl<F: FieldExt> CondSwapChip<F> {
|
||||||
/// Configures this chip for use in a circuit.
|
/// 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(
|
pub fn configure(
|
||||||
meta: &mut ConstraintSystem<F>,
|
meta: &mut ConstraintSystem<F>,
|
||||||
advices: [Column<Advice>; 5],
|
advices: [Column<Advice>; 5],
|
||||||
|
@ -141,44 +144,43 @@ impl<F: FieldExt> CondSwapChip<F> {
|
||||||
|
|
||||||
let config = CondSwapConfig {
|
let config = CondSwapConfig {
|
||||||
q_swap,
|
q_swap,
|
||||||
x: advices[0],
|
a: advices[0],
|
||||||
y: advices[1],
|
b: advices[1],
|
||||||
x_swapped: advices[2],
|
a_swapped: advices[2],
|
||||||
y_swapped: advices[3],
|
b_swapped: advices[3],
|
||||||
swap: advices[4],
|
swap: advices[4],
|
||||||
perm,
|
perm,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: optimise shape of gate for Merkle path validation
|
// TODO: optimise shape of gate for Merkle path validation
|
||||||
|
|
||||||
meta.create_gate("x' = y ⋅ swap + x ⋅ (1-swap)", |meta| {
|
meta.create_gate("a' = b ⋅ swap + a ⋅ (1-swap)", |meta| {
|
||||||
let q_swap = meta.query_selector(q_swap, Rotation::cur());
|
let q_swap = meta.query_selector(q_swap, Rotation::cur());
|
||||||
|
|
||||||
let x = meta.query_advice(config.x, Rotation::cur());
|
let a = meta.query_advice(config.a, Rotation::cur());
|
||||||
let y = meta.query_advice(config.y, Rotation::cur());
|
let b = meta.query_advice(config.b, Rotation::cur());
|
||||||
let x_swapped = meta.query_advice(config.x_swapped, Rotation::cur());
|
let a_swapped = meta.query_advice(config.a_swapped, Rotation::cur());
|
||||||
let y_swapped = meta.query_advice(config.y_swapped, Rotation::cur());
|
let b_swapped = meta.query_advice(config.b_swapped, Rotation::cur());
|
||||||
let swap = meta.query_advice(config.swap, Rotation::cur());
|
let swap = meta.query_advice(config.swap, Rotation::cur());
|
||||||
|
|
||||||
let one = Expression::Constant(F::one());
|
let one = Expression::Constant(F::one());
|
||||||
|
|
||||||
// x_swapped - y ⋅ swap - x ⋅ (1-swap) = 0
|
// a_swapped - b ⋅ swap - a ⋅ (1-swap) = 0
|
||||||
// This checks that `x_swapped` is equal to `y` when `swap` is set,
|
// This checks that `a_swapped` is equal to `y` when `swap` is set,
|
||||||
// but remains as `x` when `swap` is not set.
|
// but remains as `a` when `swap` is not set.
|
||||||
let x_check =
|
let a_check =
|
||||||
x_swapped - y.clone() * swap.clone() - x.clone() * (one.clone() - swap.clone());
|
a_swapped - b.clone() * swap.clone() - a.clone() * (one.clone() - swap.clone());
|
||||||
|
|
||||||
// y_swapped - x ⋅ swap - y ⋅ (1-swap) = 0
|
// b_swapped - a ⋅ swap - b ⋅ (1-swap) = 0
|
||||||
// This checks that `y_swapped` is equal to `x` when `swap` is set,
|
// This checks that `b_swapped` is equal to `a` when `swap` is set,
|
||||||
// but remains as `y` when `swap` is not set.
|
// but remains as `b` when `swap` is not set.
|
||||||
let y_check = y_swapped - x * swap.clone() - y * (one.clone() - swap.clone());
|
let b_check = b_swapped - a * swap.clone() - b * (one.clone() - swap.clone());
|
||||||
|
|
||||||
// Check `swap` is boolean.
|
// Check `swap` is boolean.
|
||||||
let bool_check = swap.clone() * (one - swap);
|
let bool_check = swap.clone() * (one - swap);
|
||||||
|
|
||||||
[x_check, y_check, bool_check]
|
array::IntoIter::new([a_check, b_check, bool_check])
|
||||||
.iter()
|
.map(|poly| q_swap.clone() * poly)
|
||||||
.map(|poly| q_swap.clone() * poly.clone())
|
|
||||||
.collect()
|
.collect()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -207,8 +209,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn cond_swap() {
|
fn cond_swap() {
|
||||||
struct MyCircuit<F: FieldExt> {
|
struct MyCircuit<F: FieldExt> {
|
||||||
x: Option<F>,
|
a: Option<F>,
|
||||||
y: Option<F>,
|
b: Option<F>,
|
||||||
swap: Option<bool>,
|
swap: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,21 +245,20 @@ mod tests {
|
||||||
let chip = CondSwapChip::<F>::construct(config.clone());
|
let chip = CondSwapChip::<F>::construct(config.clone());
|
||||||
|
|
||||||
// Load the pair and the swap flag into the circuit.
|
// Load the pair and the swap flag into the circuit.
|
||||||
let x = chip.load_private(layouter.namespace(|| "x"), config.x, self.x)?;
|
let a = chip.load_private(layouter.namespace(|| "a"), config.a, self.a)?;
|
||||||
let y = chip.load_private(layouter.namespace(|| "y"), config.y, self.y)?;
|
let b = chip.load_private(layouter.namespace(|| "b"), config.b, self.b)?;
|
||||||
// Return the swapped pair.
|
// Return the swapped pair.
|
||||||
let swapped_pair =
|
let swapped_pair = chip.swap(layouter.namespace(|| "swap"), (a, b), self.swap)?;
|
||||||
chip.swap(layouter.namespace(|| "swap"), (x, y).into(), self.swap)?;
|
|
||||||
|
|
||||||
if let Some(swap) = self.swap {
|
if let Some(swap) = self.swap {
|
||||||
if swap {
|
if swap {
|
||||||
// Check that `x` and `y` have been swapped
|
// Check that `a` and `b` have been swapped
|
||||||
assert_eq!(swapped_pair.0.value.unwrap(), y.value.unwrap());
|
assert_eq!(swapped_pair.0.value.unwrap(), b.value.unwrap());
|
||||||
assert_eq!(swapped_pair.1.value.unwrap(), x.value.unwrap());
|
assert_eq!(swapped_pair.1.value.unwrap(), a.value.unwrap());
|
||||||
} else {
|
} else {
|
||||||
// Check that `x` and `y` have not been swapped
|
// Check that `a` and `b` have not been swapped
|
||||||
assert_eq!(swapped_pair.0.value.unwrap(), x.value.unwrap());
|
assert_eq!(swapped_pair.0.value.unwrap(), a.value.unwrap());
|
||||||
assert_eq!(swapped_pair.1.value.unwrap(), y.value.unwrap());
|
assert_eq!(swapped_pair.1.value.unwrap(), b.value.unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,28 +269,22 @@ mod tests {
|
||||||
// Test swap case
|
// Test swap case
|
||||||
{
|
{
|
||||||
let circuit: MyCircuit<Base> = MyCircuit {
|
let circuit: MyCircuit<Base> = MyCircuit {
|
||||||
x: Some(Base::rand()),
|
a: Some(Base::rand()),
|
||||||
y: Some(Base::rand()),
|
b: Some(Base::rand()),
|
||||||
swap: Some(true),
|
swap: Some(true),
|
||||||
};
|
};
|
||||||
let prover = match MockProver::<Base>::run(3, &circuit, vec![]) {
|
let prover = MockProver::<Base>::run(3, &circuit, vec![]).unwrap();
|
||||||
Ok(prover) => prover,
|
|
||||||
Err(e) => panic!("{:?}", e),
|
|
||||||
};
|
|
||||||
assert_eq!(prover.verify(), Ok(()));
|
assert_eq!(prover.verify(), Ok(()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test non-swap case
|
// Test non-swap case
|
||||||
{
|
{
|
||||||
let circuit: MyCircuit<Base> = MyCircuit {
|
let circuit: MyCircuit<Base> = MyCircuit {
|
||||||
x: Some(Base::rand()),
|
a: Some(Base::rand()),
|
||||||
y: Some(Base::rand()),
|
b: Some(Base::rand()),
|
||||||
swap: Some(false),
|
swap: Some(false),
|
||||||
};
|
};
|
||||||
let prover = match MockProver::<Base>::run(3, &circuit, vec![]) {
|
let prover = MockProver::<Base>::run(3, &circuit, vec![]).unwrap();
|
||||||
Ok(prover) => prover,
|
|
||||||
Err(e) => panic!("{:?}", e),
|
|
||||||
};
|
|
||||||
assert_eq!(prover.verify(), Ok(()));
|
assert_eq!(prover.verify(), Ok(()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,6 +91,9 @@ impl<F: FieldExt> EnableFlagInstructions<F> for EnableFlagChip<F> {
|
||||||
|
|
||||||
impl<F: FieldExt> EnableFlagChip<F> {
|
impl<F: FieldExt> EnableFlagChip<F> {
|
||||||
/// Configures this chip for use in a circuit.
|
/// 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(
|
pub fn configure(
|
||||||
meta: &mut ConstraintSystem<F>,
|
meta: &mut ConstraintSystem<F>,
|
||||||
advices: [Column<Advice>; 2],
|
advices: [Column<Advice>; 2],
|
||||||
|
@ -183,10 +186,7 @@ mod tests {
|
||||||
value: Some(Base::one()),
|
value: Some(Base::one()),
|
||||||
enable_flag: Some(true),
|
enable_flag: Some(true),
|
||||||
};
|
};
|
||||||
let prover = match MockProver::<Base>::run(1, &circuit, vec![]) {
|
let prover = MockProver::<Base>::run(1, &circuit, vec![]).unwrap();
|
||||||
Ok(prover) => prover,
|
|
||||||
Err(e) => panic!("{:?}", e),
|
|
||||||
};
|
|
||||||
assert_eq!(prover.verify(), Ok(()));
|
assert_eq!(prover.verify(), Ok(()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,10 +196,7 @@ mod tests {
|
||||||
value: Some(Base::zero()),
|
value: Some(Base::zero()),
|
||||||
enable_flag: Some(false),
|
enable_flag: Some(false),
|
||||||
};
|
};
|
||||||
let prover = match MockProver::<Base>::run(1, &circuit, vec![]) {
|
let prover = MockProver::<Base>::run(1, &circuit, vec![]).unwrap();
|
||||||
Ok(prover) => prover,
|
|
||||||
Err(e) => panic!("{:?}", e),
|
|
||||||
};
|
|
||||||
assert_eq!(prover.verify(), Ok(()));
|
assert_eq!(prover.verify(), Ok(()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,10 +206,7 @@ mod tests {
|
||||||
value: Some(Base::zero()),
|
value: Some(Base::zero()),
|
||||||
enable_flag: Some(true),
|
enable_flag: Some(true),
|
||||||
};
|
};
|
||||||
let prover = match MockProver::<Base>::run(1, &circuit, vec![]) {
|
let prover = MockProver::<Base>::run(1, &circuit, vec![]).unwrap();
|
||||||
Ok(prover) => prover,
|
|
||||||
Err(e) => panic!("{:?}", e),
|
|
||||||
};
|
|
||||||
assert_eq!(prover.verify(), Ok(()));
|
assert_eq!(prover.verify(), Ok(()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,10 +216,7 @@ mod tests {
|
||||||
value: Some(Base::one()),
|
value: Some(Base::one()),
|
||||||
enable_flag: Some(false),
|
enable_flag: Some(false),
|
||||||
};
|
};
|
||||||
let prover = match MockProver::<Base>::run(1, &circuit, vec![]) {
|
let prover = MockProver::<Base>::run(1, &circuit, vec![]).unwrap();
|
||||||
Ok(prover) => prover,
|
|
||||||
Err(e) => panic!("{:?}", e),
|
|
||||||
};
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
prover.verify(),
|
prover.verify(),
|
||||||
Err(vec![VerifyFailure::Gate {
|
Err(vec![VerifyFailure::Gate {
|
||||||
|
|
|
@ -170,6 +170,9 @@ impl<F: FieldExt> PLONKInstructions<F> for PLONKChip<F> {
|
||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
impl<F: FieldExt> PLONKChip<F> {
|
impl<F: FieldExt> PLONKChip<F> {
|
||||||
/// Configures this chip for use in a circuit.
|
/// 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(
|
pub fn configure(
|
||||||
meta: &mut ConstraintSystem<F>,
|
meta: &mut ConstraintSystem<F>,
|
||||||
advices: [Column<Advice>; 3],
|
advices: [Column<Advice>; 3],
|
||||||
|
@ -335,10 +338,7 @@ mod tests {
|
||||||
a: Some(Base::rand()),
|
a: Some(Base::rand()),
|
||||||
b: Some(Base::rand()),
|
b: Some(Base::rand()),
|
||||||
};
|
};
|
||||||
let prover = match MockProver::<Base>::run(3, &circuit, vec![]) {
|
let prover = MockProver::<Base>::run(3, &circuit, vec![]).unwrap();
|
||||||
Ok(prover) => prover,
|
|
||||||
Err(e) => panic!("{:?}", e),
|
|
||||||
};
|
|
||||||
assert_eq!(prover.verify(), Ok(()));
|
assert_eq!(prover.verify(), Ok(()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue