1use field_cat::{Field, FieldBytes};
13use plonkish_cat::{Constraint, ConstraintSet, Expression, Wire};
14use proof_cat_core::commit::merkle::{MerkleProof, MerkleRoot, MerkleTree};
15use proof_cat_core::{
16 MultilinearPoly, NumVars, SumcheckClaim, SumcheckProof, Transcript, sumcheck_prove,
17 sumcheck_verify,
18};
19
20use crate::error::Error;
21
22const TRANSCRIPT_LABEL: &[u8] = b"proof-cat-v0.1";
24
25#[derive(Debug, Clone)]
44pub struct Witness<F: Field> {
45 values: Vec<F>,
46}
47
48impl<F: Field> Witness<F> {
49 #[must_use]
51 pub fn new(values: Vec<F>) -> Self {
52 Self { values }
53 }
54
55 #[must_use]
57 pub fn values(&self) -> &[F] {
58 &self.values
59 }
60
61 fn assignment(&self) -> impl Fn(Wire) -> Result<F, plonkish_cat::Error> + '_ {
63 |wire| {
64 self.values
65 .get(wire.index())
66 .cloned()
67 .ok_or(plonkish_cat::Error::WireOutOfBounds {
68 wire_index: wire.index(),
69 allocated: self.values.len(),
70 })
71 }
72 }
73}
74
75#[derive(Debug, Clone)]
77pub struct WireOpening<F: Field> {
78 wire_index: usize,
79 value: F,
80 merkle_proof: MerkleProof,
81}
82
83impl<F: Field> WireOpening<F> {
84 #[must_use]
86 pub fn wire_index(&self) -> usize {
87 self.wire_index
88 }
89
90 #[must_use]
92 pub fn value(&self) -> &F {
93 &self.value
94 }
95
96 #[must_use]
98 pub fn merkle_proof(&self) -> &MerkleProof {
99 &self.merkle_proof
100 }
101}
102
103#[derive(Debug, Clone)]
105pub struct Proof<F: Field> {
106 witness_commitment: MerkleRoot,
107 sumcheck: SumcheckProof<F>,
108 wire_openings: Vec<WireOpening<F>>,
109}
110
111impl<F: Field> Proof<F> {
112 #[must_use]
114 pub fn witness_commitment(&self) -> &MerkleRoot {
115 &self.witness_commitment
116 }
117
118 #[must_use]
120 pub fn sumcheck_proof(&self) -> &SumcheckProof<F> {
121 &self.sumcheck
122 }
123
124 #[must_use]
126 pub fn wire_openings(&self) -> &[WireOpening<F>] {
127 &self.wire_openings
128 }
129}
130
131pub 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(&SumcheckClaim::new(poly, F::zero()), transcript)?;
202
203 let wire_openings: Result<Vec<WireOpening<F>>, Error> = (0..witness.values().len())
205 .map(|i| {
206 let merkle_proof = tree.open(i)?;
207 Ok(WireOpening {
208 wire_index: i,
209 value: witness.values()[i].clone(),
210 merkle_proof,
211 })
212 })
213 .collect();
214
215 Ok(Proof {
216 witness_commitment: tree.root(),
217 sumcheck,
218 wire_openings: wire_openings?,
219 })
220 }
221}
222
223pub fn verify<F: FieldBytes>(
270 constraints: &ConstraintSet<F>,
271 proof: &Proof<F>,
272) -> Result<bool, Error> {
273 let all_constraints = flatten_constraints(constraints);
275
276 if all_constraints.is_empty() {
277 Err(Error::EmptyConstraintSet)
278 } else {
279 let padded_len = pad_to_power_of_two_len(all_constraints.len());
281 let num_vars = usize::try_from(padded_len.trailing_zeros())
282 .map_err(|_| proof_cat_core::Error::NotPowerOfTwo { value: padded_len })?;
283
284 let transcript = Transcript::new(TRANSCRIPT_LABEL)
285 .absorb_bytes(proof.witness_commitment.as_bytes())
286 .absorb_bytes(&all_constraints.len().to_le_bytes());
287
288 let (final_eval, challenges, _) = sumcheck_verify(
289 &proof.sumcheck,
290 &F::zero(),
291 NumVars::new(num_vars),
292 transcript,
293 )?;
294
295 let all_openings_valid = proof.wire_openings.iter().all(|opening| {
297 MerkleTree::verify_opening(
298 &proof.witness_commitment,
299 opening.wire_index,
300 &opening.value,
301 &opening.merkle_proof,
302 )
303 });
304
305 if all_openings_valid {
306 let assignment = build_assignment_from_openings(&proof.wire_openings);
308
309 let evals = evaluate_constraints_with(&all_constraints, &assignment)?;
311 let padded = pad_to_power_of_two(evals);
312 let poly = MultilinearPoly::from_evals(padded)?;
313
314 let expected_eval = poly.evaluate(&challenges)?;
316 Ok(expected_eval == final_eval)
317 } else {
318 Err(proof_cat_core::Error::MerkleVerificationFailed.into())
319 }
320 }
321}
322
323fn flatten_constraints<F: Field>(cs: &ConstraintSet<F>) -> Vec<Constraint<F>> {
328 let polynomial: Vec<Constraint<F>> = cs.constraints().to_vec();
329 let from_copies: Vec<Constraint<F>> = cs
330 .copy_constraints()
331 .iter()
332 .map(|cc| {
333 let expr = Expression::Wire(cc.left()) - Expression::Wire(cc.right());
334 Constraint::new(expr)
335 })
336 .collect();
337 polynomial.into_iter().chain(from_copies).collect()
338}
339
340fn validate_witness<F: Field>(
342 constraints: &[Constraint<F>],
343 witness: &Witness<F>,
344) -> Result<(), Error> {
345 let assign = witness.assignment();
346 constraints.iter().enumerate().try_for_each(|(i, c)| {
347 c.is_satisfied(&assign).map_err(Error::from).and_then(|ok| {
348 if ok {
349 Ok(())
350 } else {
351 Err(Error::UnsatisfiedConstraint { index: i })
352 }
353 })
354 })
355}
356
357fn evaluate_constraints<F: Field>(
359 constraints: &[Constraint<F>],
360 witness: &Witness<F>,
361) -> Result<Vec<F>, Error> {
362 let assign = witness.assignment();
363 constraints
364 .iter()
365 .map(|c| c.expression().evaluate(&assign).map_err(Error::from))
366 .collect()
367}
368
369fn evaluate_constraints_with<F: Field>(
371 constraints: &[Constraint<F>],
372 assignment: &impl Fn(Wire) -> Result<F, plonkish_cat::Error>,
373) -> Result<Vec<F>, Error> {
374 constraints
375 .iter()
376 .map(|c| c.expression().evaluate(assignment).map_err(Error::from))
377 .collect()
378}
379
380fn build_assignment_from_openings<F: Field>(
382 openings: &[WireOpening<F>],
383) -> impl Fn(Wire) -> Result<F, plonkish_cat::Error> + '_ {
384 move |wire| {
385 openings
386 .iter()
387 .find(|o| o.wire_index == wire.index())
388 .map(|o| o.value.clone())
389 .ok_or(plonkish_cat::Error::WireOutOfBounds {
390 wire_index: wire.index(),
391 allocated: openings.len(),
392 })
393 }
394}
395
396fn pad_to_power_of_two<F: Field>(v: Vec<F>) -> Vec<F> {
398 let target = pad_to_power_of_two_len(v.len());
399 let padding_count = target - v.len();
400 v.into_iter()
401 .chain((0..padding_count).map(|_| F::zero()))
402 .collect()
403}
404
405fn pad_to_power_of_two_len(n: usize) -> usize {
407 if n <= 1 { 1 } else { n.next_power_of_two() }
408}
409
410#[cfg(test)]
411mod tests {
412 use super::*;
413 use field_cat::F101;
414 use plonkish_cat::{CopyConstraint, Expression, Wire};
415
416 #[test]
417 fn add_gate_prove_verify() -> Result<(), Error> {
418 let expr = Expression::Wire(Wire::new(2))
421 - Expression::Wire(Wire::new(0))
422 - Expression::Wire(Wire::new(1));
423 let cs = ConstraintSet::empty().with_constraint(Constraint::new(expr));
424
425 let witness = Witness::new(vec![F101::new(3), F101::new(4), F101::new(7)]);
427 let proof = prove(&cs, &witness)?;
428 let valid = verify(&cs, &proof)?;
429 assert!(valid);
430 Ok(())
431 }
432
433 #[test]
434 fn mul_gate_prove_verify() -> Result<(), Error> {
435 let expr = Expression::Wire(Wire::new(2))
437 - Expression::Wire(Wire::new(0)) * Expression::Wire(Wire::new(1));
438 let cs = ConstraintSet::empty().with_constraint(Constraint::new(expr));
439
440 let witness = Witness::new(vec![F101::new(5), F101::new(6), F101::new(30)]);
442 let proof = prove(&cs, &witness)?;
443 assert!(verify(&cs, &proof)?);
444 Ok(())
445 }
446
447 #[test]
448 fn invalid_witness_rejected() {
449 let expr = Expression::Wire(Wire::new(2))
450 - Expression::Wire(Wire::new(0))
451 - Expression::Wire(Wire::new(1));
452 let cs = ConstraintSet::empty().with_constraint(Constraint::new(expr));
453
454 let witness = Witness::new(vec![F101::new(3), F101::new(4), F101::new(8)]);
456 let result = prove(&cs, &witness);
457 assert!(result.is_err());
458 }
459
460 #[test]
461 fn copy_constraint_prove_verify() -> Result<(), Error> {
462 let cs = ConstraintSet::empty().with_copy(CopyConstraint::new(Wire::new(0), Wire::new(1)));
464
465 let witness = Witness::new(vec![F101::new(42), F101::new(42)]);
466 let proof = prove(&cs, &witness)?;
467 assert!(verify(&cs, &proof)?);
468 Ok(())
469 }
470
471 #[test]
472 fn copy_constraint_invalid_rejected() {
473 let cs = ConstraintSet::empty().with_copy(CopyConstraint::new(Wire::new(0), Wire::new(1)));
474
475 let witness = Witness::new(vec![F101::new(1), F101::new(2)]);
476 let result = prove(&cs, &witness);
477 assert!(result.is_err());
478 }
479
480 #[test]
481 fn const_gate_prove_verify() -> Result<(), Error> {
482 let expr = Expression::Wire(Wire::new(0)) - Expression::Constant(F101::new(42));
484 let cs = ConstraintSet::empty().with_constraint(Constraint::new(expr));
485
486 let witness = Witness::new(vec![F101::new(42)]);
487 let proof = prove(&cs, &witness)?;
488 assert!(verify(&cs, &proof)?);
489 Ok(())
490 }
491}