Documentation improvements and minor refactors.

Co-authored-by: Jack Grigg <jack@electriccoin.co>
This commit is contained in:
therealyingtong 2021-06-08 00:28:32 +08:00
parent 0f2dfc5508
commit 54c8cfd1d0
4 changed files with 91 additions and 102 deletions

View File

@ -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)
})?; })?;

View File

@ -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(()));
} }
} }

View File

@ -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 {

View File

@ -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(()));
} }
} }