1use plonkish_cat::{
13 Constraint, ConstraintSet, Expression, Field, Wire,
14};
15
16use crate::commit::merkle::{MerkleProof, MerkleRoot, MerkleTree};
17use crate::error::Error;
18use crate::field::FieldBytes;
19use crate::poly::MultilinearPoly;
20use crate::sumcheck::{SumcheckClaim, SumcheckProof, sumcheck_prove, sumcheck_verify};
21use crate::transcript::Transcript;
22
23const TRANSCRIPT_LABEL: &[u8] = b"proof-cat-v0.1";
25
26#[derive(Debug, Clone)]
45pub struct Witness<F: Field> {
46 values: Vec<F>,
47}
48
49impl<F: Field> Witness<F> {
50 #[must_use]
52 pub fn new(values: Vec<F>) -> Self {
53 Self { values }
54 }
55
56 #[must_use]
58 pub fn values(&self) -> &[F] {
59 &self.values
60 }
61
62 fn assignment(&self) -> impl Fn(Wire) -> Result<F, plonkish_cat::Error> + '_ {
64 |wire| {
65 self.values
66 .get(wire.index())
67 .cloned()
68 .ok_or(plonkish_cat::Error::WireOutOfBounds {
69 wire_index: wire.index(),
70 allocated: self.values.len(),
71 })
72 }
73 }
74}
75
76#[derive(Debug, Clone)]
78pub struct WireOpening<F: Field> {
79 wire_index: usize,
80 value: F,
81 merkle_proof: MerkleProof,
82}
83
84impl<F: Field> WireOpening<F> {
85 #[must_use]
87 pub fn wire_index(&self) -> usize {
88 self.wire_index
89 }
90
91 #[must_use]
93 pub fn value(&self) -> &F {
94 &self.value
95 }
96
97 #[must_use]
99 pub fn merkle_proof(&self) -> &MerkleProof {
100 &self.merkle_proof
101 }
102}
103
104#[derive(Debug, Clone)]
106pub struct Proof<F: Field> {
107 witness_commitment: MerkleRoot,
108 sumcheck: SumcheckProof<F>,
109 wire_openings: Vec<WireOpening<F>>,
110}
111
112impl<F: Field> Proof<F> {
113 #[must_use]
115 pub fn witness_commitment(&self) -> &MerkleRoot {
116 &self.witness_commitment
117 }
118
119 #[must_use]
121 pub fn sumcheck_proof(&self) -> &SumcheckProof<F> {
122 &self.sumcheck
123 }
124
125 #[must_use]
127 pub fn wire_openings(&self) -> &[WireOpening<F>] {
128 &self.wire_openings
129 }
130}
131
132pub fn prove<F: FieldBytes>(
174 constraints: &ConstraintSet<F>,
175 witness: &Witness<F>,
176) -> Result<Proof<F>, Error> {
177 let all_constraints = flatten_constraints(constraints);
179
180 if all_constraints.is_empty() {
181 Err(Error::EmptyConstraintSet)
182 } else {
183 validate_witness(&all_constraints, witness)?;
185
186 let tree = MerkleTree::from_field_values(witness.values());
188
189 let evals = evaluate_constraints(&all_constraints, witness)?;
191
192 let padded = pad_to_power_of_two(evals);
194 let poly = MultilinearPoly::from_evals(padded)?;
195
196 let transcript = Transcript::new(TRANSCRIPT_LABEL)
198 .absorb_bytes(tree.root().as_bytes())
199 .absorb_bytes(&all_constraints.len().to_le_bytes());
200
201 let (sumcheck, _, _) = sumcheck_prove(
202 &SumcheckClaim::new(poly, F::zero()),
203 transcript,
204 )?;
205
206 let wire_openings: Result<Vec<WireOpening<F>>, Error> = (0..witness.values().len())
208 .map(|i| {
209 let merkle_proof = tree.open(i)?;
210 Ok(WireOpening {
211 wire_index: i,
212 value: witness.values()[i].clone(),
213 merkle_proof,
214 })
215 })
216 .collect();
217
218 Ok(Proof {
219 witness_commitment: tree.root(),
220 sumcheck,
221 wire_openings: wire_openings?,
222 })
223 }
224}
225
226pub fn verify<F: FieldBytes>(
272 constraints: &ConstraintSet<F>,
273 proof: &Proof<F>,
274) -> Result<bool, Error> {
275 let all_constraints = flatten_constraints(constraints);
277
278 if all_constraints.is_empty() {
279 Err(Error::EmptyConstraintSet)
280 } else {
281 let padded_len = pad_to_power_of_two_len(all_constraints.len());
283 let num_vars = usize::try_from(padded_len.trailing_zeros())
284 .map_err(|_| Error::NotPowerOfTwo { value: padded_len })?;
285
286 let transcript = Transcript::new(TRANSCRIPT_LABEL)
287 .absorb_bytes(proof.witness_commitment.as_bytes())
288 .absorb_bytes(&all_constraints.len().to_le_bytes());
289
290 let (final_eval, challenges, _) = sumcheck_verify(
291 &proof.sumcheck,
292 &F::zero(),
293 crate::poly::NumVars::new(num_vars),
294 transcript,
295 )?;
296
297 let all_openings_valid = proof.wire_openings.iter().all(|opening| {
299 MerkleTree::verify_opening(
300 &proof.witness_commitment,
301 opening.wire_index,
302 &opening.value,
303 &opening.merkle_proof,
304 )
305 });
306
307 if all_openings_valid {
308 let assignment = build_assignment_from_openings(&proof.wire_openings);
310
311 let evals = evaluate_constraints_with(
313 &all_constraints,
314 &assignment,
315 )?;
316 let padded = pad_to_power_of_two(evals);
317 let poly = MultilinearPoly::from_evals(padded)?;
318
319 let expected_eval = poly.evaluate(&challenges)?;
321 Ok(expected_eval == final_eval)
322 } else {
323 Err(Error::MerkleVerificationFailed)
324 }
325 }
326}
327
328fn flatten_constraints<F: Field>(cs: &ConstraintSet<F>) -> Vec<Constraint<F>> {
333 let polynomial: Vec<Constraint<F>> = cs.constraints().to_vec();
334 let from_copies: Vec<Constraint<F>> = cs
335 .copy_constraints()
336 .iter()
337 .map(|cc| {
338 let expr = Expression::Wire(cc.left()) - Expression::Wire(cc.right());
339 Constraint::new(expr)
340 })
341 .collect();
342 polynomial
343 .into_iter()
344 .chain(from_copies)
345 .collect()
346}
347
348fn validate_witness<F: Field>(
350 constraints: &[Constraint<F>],
351 witness: &Witness<F>,
352) -> Result<(), Error> {
353 let assign = witness.assignment();
354 constraints
355 .iter()
356 .enumerate()
357 .try_for_each(|(i, c)| {
358 c.is_satisfied(&assign)
359 .map_err(Error::from)
360 .and_then(|ok| {
361 if ok {
362 Ok(())
363 } else {
364 Err(Error::UnsatisfiedConstraint { index: i })
365 }
366 })
367 })
368}
369
370fn evaluate_constraints<F: Field>(
372 constraints: &[Constraint<F>],
373 witness: &Witness<F>,
374) -> Result<Vec<F>, Error> {
375 let assign = witness.assignment();
376 constraints
377 .iter()
378 .map(|c| c.expression().evaluate(&assign).map_err(Error::from))
379 .collect()
380}
381
382fn evaluate_constraints_with<F: Field>(
384 constraints: &[Constraint<F>],
385 assignment: &impl Fn(Wire) -> Result<F, plonkish_cat::Error>,
386) -> Result<Vec<F>, Error> {
387 constraints
388 .iter()
389 .map(|c| c.expression().evaluate(assignment).map_err(Error::from))
390 .collect()
391}
392
393fn build_assignment_from_openings<F: Field>(
395 openings: &[WireOpening<F>],
396) -> impl Fn(Wire) -> Result<F, plonkish_cat::Error> + '_ {
397 move |wire| {
398 openings
399 .iter()
400 .find(|o| o.wire_index == wire.index())
401 .map(|o| o.value.clone())
402 .ok_or(plonkish_cat::Error::WireOutOfBounds {
403 wire_index: wire.index(),
404 allocated: openings.len(),
405 })
406 }
407}
408
409fn pad_to_power_of_two<F: Field>(v: Vec<F>) -> Vec<F> {
411 let target = pad_to_power_of_two_len(v.len());
412 let padding_count = target - v.len();
413 v.into_iter()
414 .chain((0..padding_count).map(|_| F::zero()))
415 .collect()
416}
417
418fn pad_to_power_of_two_len(n: usize) -> usize {
420 if n <= 1 { 1 } else { n.next_power_of_two() }
421}
422
423#[cfg(test)]
424mod tests {
425 use super::*;
426 use plonkish_cat::{CopyConstraint, Expression, Wire, F101};
427
428 #[test]
429 fn add_gate_prove_verify() -> Result<(), Error> {
430 let expr =
433 Expression::Wire(Wire::new(2))
434 - Expression::Wire(Wire::new(0))
435 - Expression::Wire(Wire::new(1));
436 let cs = ConstraintSet::empty()
437 .with_constraint(Constraint::new(expr));
438
439 let witness = Witness::new(vec![
441 F101::new(3),
442 F101::new(4),
443 F101::new(7),
444 ]);
445 let proof = prove(&cs, &witness)?;
446 let valid = verify(&cs, &proof)?;
447 assert!(valid);
448 Ok(())
449 }
450
451 #[test]
452 fn mul_gate_prove_verify() -> Result<(), Error> {
453 let expr = Expression::Wire(Wire::new(2))
455 - Expression::Wire(Wire::new(0)) * Expression::Wire(Wire::new(1));
456 let cs = ConstraintSet::empty()
457 .with_constraint(Constraint::new(expr));
458
459 let witness = Witness::new(vec![
461 F101::new(5),
462 F101::new(6),
463 F101::new(30),
464 ]);
465 let proof = prove(&cs, &witness)?;
466 assert!(verify(&cs, &proof)?);
467 Ok(())
468 }
469
470 #[test]
471 fn invalid_witness_rejected() {
472 let expr =
473 Expression::Wire(Wire::new(2))
474 - Expression::Wire(Wire::new(0))
475 - Expression::Wire(Wire::new(1));
476 let cs = ConstraintSet::empty()
477 .with_constraint(Constraint::new(expr));
478
479 let witness = Witness::new(vec![
481 F101::new(3),
482 F101::new(4),
483 F101::new(8),
484 ]);
485 let result = prove(&cs, &witness);
486 assert!(result.is_err());
487 }
488
489 #[test]
490 fn copy_constraint_prove_verify() -> Result<(), Error> {
491 let cs = ConstraintSet::empty()
493 .with_copy(CopyConstraint::new(Wire::new(0), Wire::new(1)));
494
495 let witness = Witness::new(vec![F101::new(42), F101::new(42)]);
496 let proof = prove(&cs, &witness)?;
497 assert!(verify(&cs, &proof)?);
498 Ok(())
499 }
500
501 #[test]
502 fn copy_constraint_invalid_rejected() {
503 let cs = ConstraintSet::empty()
504 .with_copy(CopyConstraint::new(Wire::new(0), Wire::new(1)));
505
506 let witness = Witness::new(vec![F101::new(1), F101::new(2)]);
507 let result = prove(&cs, &witness);
508 assert!(result.is_err());
509 }
510
511 #[test]
512 fn const_gate_prove_verify() -> Result<(), Error> {
513 let expr = Expression::Wire(Wire::new(0)) - Expression::Constant(F101::new(42));
515 let cs = ConstraintSet::empty()
516 .with_constraint(Constraint::new(expr));
517
518 let witness = Witness::new(vec![F101::new(42)]);
519 let proof = prove(&cs, &witness)?;
520 assert!(verify(&cs, &proof)?);
521 Ok(())
522 }
523}