Skip to content

Commit be94fe4

Browse files
authored
Merge pull request #197 from o1-labs/mimoo/rand
[kimchi][proof] add zero-knowledge rows in witness
2 parents 160ab8d + 7c5bd58 commit be94fe4

File tree

15 files changed

+207
-184
lines changed

15 files changed

+207
-184
lines changed

circuits/plonk-15-wires/src/gate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,7 +493,7 @@ pub mod caml {
493493
T1: Clone,
494494
T2: From<T1>,
495495
{
496-
std::array::IntoIter::new(a)
496+
a.into_iter()
497497
.map(Into::into)
498498
.next_tuple()
499499
.expect("bug in array_to_tuple")

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

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ use oracle::poseidon::ArithmeticSpongeParams;
2121
use serde::{de::DeserializeOwned, Deserialize, Serialize};
2222
use serde_with::serde_as;
2323

24+
//
25+
// Constants
26+
//
27+
28+
pub const ZK_ROWS: u64 = 3;
29+
30+
//
31+
// ConstraintSystem
32+
//
33+
2434
#[serde_as]
2535
#[derive(Clone, Serialize, Deserialize, Debug)]
2636
pub struct ConstraintSystem<F: FftField> {
@@ -255,7 +265,7 @@ where
255265
256266
/// Returns the end of the circuit, which is used for introducing zero-knowledge in the permutation polynomial
257267
pub fn zk_w3<F: FftField>(domain: D<F>) -> F {
258-
domain.group_gen.pow(&[domain.size - 3])
268+
domain.group_gen.pow(&[domain.size - (ZK_ROWS)])
259269
}
260270

261271
/// Evaluates the polynomial
@@ -270,7 +280,7 @@ pub fn eval_zk_polynomial<F: FftField>(domain: D<F>, x: F) -> F {
270280
/// Evaluates the polynomial
271281
/// (x - w^{n - 4}) (x - w^{n - 3}) * (x - w^{n - 2}) * (x - w^{n - 1})
272282
pub fn eval_vanishes_on_last_4_rows<F: FftField>(domain: D<F>, x: F) -> F {
273-
let w4 = domain.group_gen.pow(&[domain.size - 4]);
283+
let w4 = domain.group_gen.pow(&[domain.size - (ZK_ROWS + 1)]);
274284
let w3 = domain.group_gen * w4;
275285
let w2 = domain.group_gen * w3;
276286
let w1 = domain.group_gen * w2;
@@ -282,7 +292,7 @@ pub fn eval_vanishes_on_last_4_rows<F: FftField>(domain: D<F>, x: F) -> F {
282292
pub fn vanishes_on_last_4_rows<F: FftField>(domain: D<F>) -> DP<F> {
283293
let x = DP::from_coefficients_slice(&[F::zero(), F::one()]);
284294
let c = |a: F| DP::from_coefficients_slice(&[a]);
285-
let w4 = domain.group_gen.pow(&[domain.size - 4]);
295+
let w4 = domain.group_gen.pow(&[domain.size - (ZK_ROWS + 1)]);
286296
let w3 = domain.group_gen * w4;
287297
let w2 = domain.group_gen * w3;
288298
let w1 = domain.group_gen * w2;
@@ -332,9 +342,8 @@ impl<F: FftField + SquareRootField> ConstraintSystem<F> {
332342

333343
// +3 on gates.len() here to ensure that we have room for the zero-knowledge entries of the permutation polynomial
334344
// see https://minaprotocol.com/blog/a-more-efficient-approach-to-zero-knowledge-for-plonk
335-
// TODO: hardcode this value somewhere
336-
let domain = EvaluationDomains::<F>::create(gates.len() + 3)?;
337-
assert!(domain.d1.size > 3);
345+
let domain = EvaluationDomains::<F>::create(gates.len() + ZK_ROWS as usize)?;
346+
assert!(domain.d1.size > ZK_ROWS);
338347

339348
// pre-compute all the elements
340349
let mut sid = domain.d1.elements().map(|elm| elm).collect::<Vec<_>>();
@@ -599,6 +608,15 @@ impl<F: FftField + SquareRootField> ConstraintSystem<F> {
599608
pub fn verify(&self, witness: &[Vec<F>; COLUMNS]) -> Result<(), GateError> {
600609
let left_wire = vec![F::one(), F::zero(), F::zero(), F::zero(), F::zero()];
601610

611+
// pad the witness
612+
let pad = vec![F::zero(); self.domain.d1.size as usize - witness[0].len()];
613+
let witness: [Vec<F>; COLUMNS] = array_init(|i| {
614+
let mut w = witness[i].to_vec();
615+
w.extend_from_slice(&pad);
616+
w
617+
});
618+
619+
// check each rows' wiring
602620
for (row, gate) in self.gates.iter().enumerate() {
603621
// check if wires are connected
604622
for col in 0..PERMUTS {
@@ -621,7 +639,8 @@ impl<F: FftField + SquareRootField> ConstraintSystem<F> {
621639
}
622640
}
623641

624-
gate.verify(witness, &self)
642+
// check the gate's satisfiability
643+
gate.verify(&witness, &self)
625644
.map_err(|err| GateError::Custom { row, err })?;
626645
}
627646

circuits/plonk-15-wires/src/polynomials/chacha.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -435,16 +435,13 @@ mod tests {
435435
use super::*;
436436
use crate::polynomials::chacha::constraint;
437437
use crate::{
438-
expr::{Column, Constants, Expr, Linearization, PolishToken},
439-
gate::{LookupInfo, LookupsUsed},
440-
gates::poseidon::ROUNDS_PER_ROW,
441-
nolookup::constraints::{zk_w3, ConstraintSystem},
438+
expr::{Column, Constants, PolishToken},
439+
gate::LookupInfo,
442440
nolookup::scalars::{LookupEvaluations, ProofEvaluations},
443-
polynomials::{chacha, lookup},
444441
wires::*,
445442
};
446443
use ark_ff::UniformRand;
447-
use ark_poly::{univariate::DensePolynomial, EvaluationDomain, Radix2EvaluationDomain as D};
444+
use ark_poly::{EvaluationDomain, Radix2EvaluationDomain as D};
448445
use array_init::array_init;
449446
use mina_curves::pasta::fp::Fp as F;
450447
use rand::{rngs::StdRng, SeedableRng};
@@ -491,10 +488,10 @@ mod tests {
491488

492489
let expr = constraint::<F>(10);
493490
let linearized = expr.linearize(evaluated_cols).unwrap();
494-
let expr_polish = expr.to_polish();
491+
let _expr_polish = expr.to_polish();
495492
let linearized_polish = linearized.map(|e| e.to_polish());
496493

497-
let mut rng = &mut StdRng::from_seed([0u8; 32]);
494+
let rng = &mut StdRng::from_seed([0u8; 32]);
498495

499496
let d = D::new(1024).unwrap();
500497

circuits/plonk-15-wires/src/polynomials/endomul_scalar.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -183,18 +183,18 @@ pub fn witness<F: PrimeField + std::fmt::Display>(
183183
let one = F::one();
184184
let neg_one = -one;
185185

186-
for (i, row_bits) in bits_msb[..].chunks(bits_per_row).enumerate() {
187-
let row = row0 + i;
188-
w[0][row] = n;
189-
w[2][row] = a;
190-
w[3][row] = b;
186+
let mut row = row0;
187+
for row_bits in bits_msb[..].chunks(bits_per_row) {
188+
w[0].push(n);
189+
w[2].push(a);
190+
w[3].push(b);
191191

192192
for (j, crumb_bits) in row_bits.chunks(2).enumerate() {
193193
let b0 = *crumb_bits[1];
194194
let b1 = *crumb_bits[0];
195195

196196
let crumb = F::from(b0 as u64) + F::from(b1 as u64).double();
197-
w[6 + j][row] = crumb;
197+
w[6 + j].push(crumb);
198198

199199
a.double_in_place();
200200
b.double_in_place();
@@ -213,9 +213,13 @@ pub fn witness<F: PrimeField + std::fmt::Display>(
213213
n += crumb;
214214
}
215215

216-
w[1][row] = n;
217-
w[4][row] = a;
218-
w[5][row] = b;
216+
w[1].push(n);
217+
w[4].push(a);
218+
w[5].push(b);
219+
220+
w[14].push(F::zero()); // unused
221+
222+
row += 1;
219223
}
220224

221225
assert_eq!(x, n);

circuits/plonk-15-wires/src/polynomials/permutation.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use ark_poly::{
1616
use ark_poly::{Polynomial, UVPolynomial};
1717
use o1_utils::{ExtendedDensePolynomial, ExtendedEvaluations};
1818
use oracle::rndoracle::ProofError;
19-
use rand::rngs::ThreadRng;
19+
use rand::{CryptoRng, RngCore};
2020

2121
impl<F: FftField + SquareRootField> ConstraintSystem<F> {
2222
/// permutation quotient poly contribution computation
@@ -122,7 +122,7 @@ impl<F: FftField + SquareRootField> ConstraintSystem<F> {
122122
witness: &[Vec<F>; COLUMNS],
123123
beta: &F,
124124
gamma: &F,
125-
rng: &mut ThreadRng,
125+
rng: &mut (impl RngCore + CryptoRng),
126126
) -> Result<DensePolynomial<F>, ProofError> {
127127
let n = self.domain.d1.size as usize;
128128

dlog/plonk-15-wires/src/prover.rs

Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ This source file implements prover's zk-proof primitive.
77
pub use super::{index::Index, range};
88
use crate::plonk_sponge::FrSponge;
99
use ark_ec::AffineCurve;
10+
use ark_ff::UniformRand;
1011
use ark_ff::{FftField, Field, One, Zero};
1112
use ark_poly::{
1213
univariate::DensePolynomial, Evaluations, Polynomial, Radix2EvaluationDomain as D, UVPolynomial,
@@ -18,14 +19,14 @@ use commitment_dlog::commitment::{
1819
use lookup::CombinedEntry;
1920
use o1_utils::ExtendedDensePolynomial;
2021
use oracle::{rndoracle::ProofError, sponge::ScalarChallenge, FqSponge};
22+
use plonk_15_wires_circuits::nolookup::constraints::ZK_ROWS;
2123
use plonk_15_wires_circuits::{
2224
expr::{l0_1, Constants, Environment, LookupEnvironment},
2325
gate::{combine_table_entry, GateType, LookupInfo, LookupsUsed},
2426
nolookup::scalars::{LookupEvaluations, ProofEvaluations},
2527
polynomials::{chacha, complete_add, endomul_scalar, endosclmul, lookup, poseidon, varbasemul},
2628
wires::{COLUMNS, PERMUTS},
2729
};
28-
use rand::thread_rng;
2930
use std::collections::HashMap;
3031

3132
type Fr<G> = <G as AffineCurve>::ScalarField;
@@ -105,18 +106,42 @@ where
105106
// RETURN: prover's zk-proof
106107
pub fn create<EFqSponge: Clone + FqSponge<Fq<G>, G, Fr<G>>, EFrSponge: FrSponge<Fr<G>>>(
107108
group_map: &G::Map,
108-
witness: &[Vec<Fr<G>>; COLUMNS],
109+
mut witness: [Vec<Fr<G>>; COLUMNS],
109110
index: &Index<G>,
110111
prev_challenges: Vec<(Vec<Fr<G>>, PolyComm<G>)>,
111112
) -> Result<Self, ProofError> {
112-
let d1 = index.cs.domain.d1;
113-
let n = index.cs.domain.d1.size as usize;
114-
for w in witness.iter() {
115-
if w.len() != n {
113+
let d1_size = index.cs.domain.d1.size as usize;
114+
// TODO: rng should be passed as arg
115+
let rng = &mut rand::rngs::OsRng;
116+
117+
// double-check the witness
118+
if cfg!(test) {
119+
index.cs.verify(&witness).expect("incorrect witness");
120+
}
121+
122+
// ensure we have room for the zero-knowledge rows
123+
let length_witness = witness[0].len();
124+
let length_padding = d1_size
125+
.checked_sub(length_witness)
126+
.ok_or_else(|| ProofError::NoRoomForZkInWitness)?;
127+
if length_padding < ZK_ROWS as usize {
128+
return Err(ProofError::NoRoomForZkInWitness);
129+
}
130+
131+
// pad and add zero-knowledge rows to the witness columns
132+
for w in &mut witness {
133+
if w.len() != length_witness {
116134
return Err(ProofError::WitnessCsInconsistent);
117135
}
136+
137+
// padding
138+
w.extend(std::iter::repeat(Fr::<G>::zero()).take(length_padding));
139+
140+
// zk-rows
141+
for row in w.iter_mut().rev().take(ZK_ROWS as usize) {
142+
*row = Fr::<G>::rand(rng);
143+
}
118144
}
119-
//if index.cs.verify(witness) != true {return Err(ProofError::WitnessCsInconsistent)};
120145

121146
// the transcript of the random oracle non-interactive argument
122147
let mut fq_sponge = EFqSponge::new(index.fq_sponge_params.clone());
@@ -129,15 +154,15 @@ where
129154
)
130155
.interpolate();
131156

132-
let rng = &mut thread_rng();
133-
134157
// commit to the wire values
135158
let w_comm: [(PolyComm<G>, PolyComm<Fr<G>>); COLUMNS] = array_init(|i| {
136159
let e = Evaluations::<Fr<G>, D<Fr<G>>>::from_vec_and_domain(
137160
witness[i].clone(),
138161
index.cs.domain.d1,
139162
);
140-
index.srs.commit_evaluations(d1, &e, None, rng)
163+
index
164+
.srs
165+
.commit_evaluations(index.cs.domain.d1, &e, None, rng)
141166
});
142167

143168
// compute witness polynomials
@@ -224,7 +249,7 @@ where
224249
None => (None, None, None, None),
225250
Some(_) => {
226251
let iter_lookup_table = || {
227-
(0..n).map(|i| {
252+
(0..d1_size).map(|i| {
228253
let row = index.cs.lookup_tables8[0].iter().map(|e| &e.evals[8 * i]);
229254
CombinedEntry(combine_table_entry(joint_combiner, row))
230255
})
@@ -237,7 +262,7 @@ where
237262
dummy_lookup_value.clone(),
238263
iter_lookup_table,
239264
index.cs.lookup_table_lengths[0],
240-
d1,
265+
index.cs.domain.d1,
241266
&index.cs.gates,
242267
&witness,
243268
joint_combiner,
@@ -247,13 +272,17 @@ where
247272
.into_iter()
248273
.map(|chunk| {
249274
let v: Vec<_> = chunk.into_iter().map(|x| x.0).collect();
250-
lookup::zk_patch(v, d1, rng)
275+
lookup::zk_patch(v, index.cs.domain.d1, rng)
251276
})
252277
.collect();
253278

254279
let comm: Vec<_> = lookup_sorted
255280
.iter()
256-
.map(|v| index.srs.commit_evaluations(d1, v, None, rng))
281+
.map(|v| {
282+
index
283+
.srs
284+
.commit_evaluations(index.cs.domain.d1, v, None, rng)
285+
})
257286
.collect();
258287
let coeffs : Vec<_> =
259288
// TODO: We can avoid storing these coefficients.
@@ -279,7 +308,7 @@ where
279308
match lookup_sorted {
280309
None => (None, None, None),
281310
Some(lookup_sorted) => {
282-
let iter_lookup_table = || (0..n).map(|i| {
311+
let iter_lookup_table = || (0..d1_size).map(|i| {
283312
let row = index.cs.lookup_tables8[0].iter().map(|e| & e.evals[8 * i]);
284313
combine_table_entry(joint_combiner, row)
285314
});
@@ -288,7 +317,7 @@ where
288317
lookup::aggregation::<_, Fr<G>, _>(
289318
dummy_lookup_value.0,
290319
iter_lookup_table(),
291-
d1,
320+
index.cs.domain.d1,
292321
&index.cs.gates,
293322
&witness,
294323
joint_combiner,
@@ -297,11 +326,11 @@ where
297326
rng)?;
298327

299328
drop(lookup_sorted);
300-
if aggreg.evals[n - 4] != Fr::<G>::one() {
301-
panic!("aggregation incorrect: {}", aggreg.evals[n-3]);
329+
if aggreg.evals[d1_size - (ZK_ROWS as usize + 1)] != Fr::<G>::one() {
330+
panic!("aggregation incorrect: {}", aggreg.evals[d1_size-(ZK_ROWS as usize + 1)]);
302331
}
303332

304-
let comm = index.srs.commit_evaluations(d1, &aggreg, None, rng);
333+
let comm = index.srs.commit_evaluations(index.cs.domain.d1, &aggreg, None, rng);
305334
fq_sponge.absorb_g(&comm.0.unshifted);
306335

307336
let coeffs = aggreg.interpolate();
@@ -314,7 +343,7 @@ where
314343
};
315344

316345
// compute permutation aggregation polynomial
317-
let z = index.cs.perm_aggreg(witness, &beta, &gamma, rng)?;
346+
let z = index.cs.perm_aggreg(&witness, &beta, &gamma, rng)?;
318347
// commit to z
319348
let z_comm = index.srs.commit(&z, None, rng);
320349

@@ -381,7 +410,7 @@ where
381410
coefficient: &index.cs.coefficients8,
382411
vanishes_on_last_4_rows: &index.cs.vanishes_on_last_4_rows,
383412
z: &lagrange.d8.this.z,
384-
l0_1: l0_1(d1),
413+
l0_1: l0_1(index.cs.domain.d1),
385414
domain: index.cs.domain,
386415
index: index_evals,
387416
lookup: lookup_env,
@@ -440,7 +469,7 @@ where
440469
(t4, t8),
441470
alpha,
442471
alphas[alphas.len() - 1],
443-
lookup::constraints(&index.cs.dummy_lookup_values[0], d1)
472+
lookup::constraints(&index.cs.dummy_lookup_values[0], index.cs.domain.d1)
444473
.iter()
445474
.map(|e| e.evaluations(&env))
446475
.collect(),
@@ -606,8 +635,8 @@ where
606635
)
607636
})
608637
.collect::<Vec<_>>();
609-
let non_hiding = |n: usize| PolyComm {
610-
unshifted: vec![Fr::<G>::zero(); n],
638+
let non_hiding = |d1_size: usize| PolyComm {
639+
unshifted: vec![Fr::<G>::zero(); d1_size],
611640
shifted: None,
612641
};
613642

@@ -627,7 +656,7 @@ where
627656
// construct evaluation proof
628657
let mut polynomials = polys
629658
.iter()
630-
.map(|(p, n)| (p, None, non_hiding(*n)))
659+
.map(|(p, d1_size)| (p, None, non_hiding(*d1_size)))
631660
.collect::<Vec<_>>();
632661
polynomials.extend(vec![(&p, None, non_hiding(1))]);
633662
polynomials.extend(vec![(&ft, None, blinding_ft)]);

0 commit comments

Comments
 (0)