Skip to content

Commit 2b2fb34

Browse files
committed
[kimchi][permutation] implement an abstraction for shifts
1 parent bd0b269 commit 2b2fb34

File tree

1 file changed

+83
-50
lines changed

1 file changed

+83
-50
lines changed

circuits/plonk-15-wires/src/nolookup/constraints.rs

+83-50
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,78 @@ pub struct ConstraintSystem<F: FftField> {
173173
pub lookup_selectors: Vec<E<F, D<F>>>,
174174
}
175175

176+
/// Shifts represent the shifts required in the permutation argument of PLONK
177+
struct Shifts<F> {
178+
/// The coefficients k that create a coset when multiplied with the generator of our domain.
179+
shifts: [F; PERMUTS],
180+
/// A matrix that maps all cells coordinates {col, row} to their shifted field element.
181+
/// For example the cell {col:2, row:1} will map to omega * k2,
182+
/// which lives in map[2][1]
183+
map: [Vec<F>; PERMUTS],
184+
}
185+
186+
impl<F> Shifts<F>
187+
where
188+
F: FftField + SquareRootField,
189+
{
190+
/// Generates the shifts for a given domain
191+
pub fn new(domain: &D<F>) -> Self {
192+
let mut shifts = [F::zero(); PERMUTS];
193+
194+
// first shift is the identity
195+
shifts[0] = F::one();
196+
197+
// sample the other shifts
198+
let mut i: u32 = 7;
199+
for idx in 1..(PERMUTS) {
200+
let mut o = Self::sample(&domain, &mut i);
201+
while shifts.iter().filter(|&r| o == *r).count() > 0 {
202+
o = Self::sample(&domain, &mut i);
203+
}
204+
shifts[idx] = o;
205+
}
206+
207+
// create a map of cells to their shifted value
208+
let map: [Vec<F>; PERMUTS] =
209+
array_init(|i| domain.elements().map(|elm| shifts[i] * &elm).collect());
210+
211+
//
212+
Self { shifts, map }
213+
}
214+
215+
/// sample coordinate shifts deterministically
216+
fn sample(domain: &D<F>, i: &mut u32) -> F {
217+
let mut h = Blake2b::new();
218+
h.update(
219+
&{
220+
*i += 1;
221+
*i
222+
}
223+
.to_be_bytes(),
224+
);
225+
let mut r = F::from_random_bytes(&h.finalize()[..31]).unwrap();
226+
while r.legendre().is_qnr() == false || domain.evaluate_vanishing_polynomial(r).is_zero() {
227+
let mut h = Blake2b::new();
228+
h.update(
229+
&{
230+
*i += 1;
231+
*i
232+
}
233+
.to_be_bytes(),
234+
);
235+
r = F::from_random_bytes(&h.finalize()[..31]).unwrap();
236+
}
237+
r
238+
}
239+
240+
/// Returns the field element that represents a position
241+
fn cell_to_field(&self, &Wire { row, col }: &Wire) -> F {
242+
self.map[col][row]
243+
}
244+
}
245+
246+
///
247+
176248
/// Returns the end of the circuit, which is used for introducing zero-knowledge in the permutation polynomial
177249
pub fn zk_w3<F: FftField>(domain: D<F>) -> F {
178250
domain.group_gen.pow(&[domain.size - 3])
@@ -252,17 +324,14 @@ impl<F: FftField + SquareRootField> ConstraintSystem<F> {
252324

253325
// +3 on gates.len() here to ensure that we have room for the zero-knowledge entries of the permutation polynomial
254326
// see https://minaprotocol.com/blog/a-more-efficient-approach-to-zero-knowledge-for-plonk
327+
// TODO: hardcode this value somewhere
255328
let domain = EvaluationDomains::<F>::create(gates.len() + 3)?;
256329
assert!(domain.d1.size > 3);
257330

258331
// pre-compute all the elements
259332
let mut sid = domain.d1.elements().map(|elm| elm).collect::<Vec<_>>();
260333

261-
// sample the coordinate shifts
262-
// TODO(mimoo): should we check that the shifts are all different?
263-
let shift = Self::sample_shifts(&domain.d1, PERMUTS - 1);
264-
let shift: [F; PERMUTS] = array_init(|i| if i == 0 { F::one() } else { shift[i - 1] });
265-
334+
// pad the rows: add zero gates to reach the domain size
266335
let n = domain.d1.size();
267336
let mut padding = (gates.len()..n)
268337
.map(|i| {
@@ -277,15 +346,17 @@ impl<F: FftField + SquareRootField> ConstraintSystem<F> {
277346
.collect();
278347
gates.append(&mut padding);
279348

280-
let s: [std::vec::Vec<F>; PERMUTS] =
281-
array_init(|i| domain.d1.elements().map(|elm| shift[i] * &elm).collect());
282-
let mut sigmal1 = s.clone();
349+
// sample the coordinate shifts
350+
// TODO(mimoo): should we check that the shifts are all different?
351+
let shifts = Shifts::new(&domain.d1);
283352

284353
// compute permutation polynomials
354+
let mut sigmal1: [Vec<F>; PERMUTS] =
355+
array_init(|_| vec![F::zero(); domain.d1.size as usize]);
356+
285357
for (row, gate) in gates.iter().enumerate() {
286-
for col in 0..PERMUTS {
287-
let wire = gate.wires[col];
288-
sigmal1[col][row] = s[wire.col][wire.row];
358+
for (cell, sigma) in gate.wires.iter().zip(sigmal1.iter_mut()) {
359+
sigma[row] = shifts.cell_to_field(cell);
289360
}
290361
}
291362

@@ -497,7 +568,7 @@ impl<F: FftField + SquareRootField> ConstraintSystem<F> {
497568
zkpm,
498569
vanishes_on_last_4_rows,
499570
gates,
500-
shift,
571+
shift: shifts.shifts,
501572
endo,
502573
fr_sponge_params,
503574
})
@@ -540,44 +611,6 @@ impl<F: FftField + SquareRootField> ConstraintSystem<F> {
540611
return Ok(());
541612
}
542613

543-
/// sample coordinate shifts deterministically
544-
pub fn sample_shift(domain: &D<F>, i: &mut u32) -> F {
545-
let mut h = Blake2b::new();
546-
h.update(
547-
&{
548-
*i += 1;
549-
*i
550-
}
551-
.to_be_bytes(),
552-
);
553-
let mut r = F::from_random_bytes(&h.finalize()[..31]).unwrap();
554-
while r.legendre().is_qnr() == false || domain.evaluate_vanishing_polynomial(r).is_zero() {
555-
let mut h = Blake2b::new();
556-
h.update(
557-
&{
558-
*i += 1;
559-
*i
560-
}
561-
.to_be_bytes(),
562-
);
563-
r = F::from_random_bytes(&h.finalize()[..31]).unwrap();
564-
}
565-
r
566-
}
567-
568-
pub fn sample_shifts(domain: &D<F>, len: usize) -> Vec<F> {
569-
let mut i: u32 = 7;
570-
let mut shifts = Vec::with_capacity(len);
571-
while shifts.len() < len {
572-
let mut o = Self::sample_shift(&domain, &mut i);
573-
while shifts.iter().filter(|&r| o == *r).count() > 0 {
574-
o = Self::sample_shift(&domain, &mut i)
575-
}
576-
shifts.push(o)
577-
}
578-
shifts
579-
}
580-
581614
/// evaluate witness polynomials over domains
582615
pub fn evaluate(&self, w: &[DP<F>; COLUMNS], z: &DP<F>) -> WitnessOverDomains<F> {
583616
// compute shifted witness polynomials

0 commit comments

Comments
 (0)