zinc_piop/combined_poly_resolver/structs.rs
1use crate::combined_poly_resolver::CombinedPolyResolverError;
2use crypto_primitives::SetElement;
3use itertools::Itertools;
4use std::fmt::Debug;
5use zinc_transcript::traits::{ConstTranscribable, GenTranscribable, Transcribable};
6use zinc_utils::add;
7
8/// The proof type of the combined polynomial resolver subprotocol.
9///
10/// Note: the sumcheck proof now lives at the protocol
11/// level as part of `MultiDegreeSumcheckProof`.
12///
13/// `bit_op_evals` is the prover's claim about the bit-op virtual columns'
14/// MLE evaluations at the shared CPR point. These are *not* trusted by
15/// themselves: they are bound back to the source columns' lifted openings at
16/// the multi-point evaluation endpoint via Lemma 2.3.
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct Proof<F> {
19 /// The evaluation of the projected trace columns MLEs at the shared point.
20 pub up_evals: Vec<F>,
21 /// The evaluations of the shifted projected trace columns MLEs at the
22 /// shared point. Carries *only* the row-shift virtual columns —
23 /// bit-op virtuals are sent separately in `bit_op_evals` to avoid
24 /// overloading shift semantics on the wire.
25 pub down_evals: Vec<F>,
26 /// The evaluations of the bit-op virtual columns (ROTR / SHR) at the
27 /// shared point, in `UairSignature::bit_op_specs()` order.
28 pub bit_op_evals: Vec<F>,
29}
30
31impl<F> Proof<F> {
32 /// Maps every field element through `f`, preserving structure — used to
33 /// lift elements into wire integers and to project wire integers back
34 /// into elements at the (de)serialization boundary.
35 pub fn try_map<T, E>(&self, f: impl FnMut(&F) -> Result<T, E> + Copy) -> Result<Proof<T>, E> {
36 Ok(Proof {
37 up_evals: self.up_evals.iter().map(f).try_collect()?,
38 down_evals: self.down_evals.iter().map(f).try_collect()?,
39 bit_op_evals: self.bit_op_evals.iter().map(f).try_collect()?,
40 })
41 }
42}
43
44impl<F: ConstTranscribable> GenTranscribable for Proof<F> {
45 fn read_transcription_bytes_exact(bytes: &[u8]) -> Self {
46 let (up_evals, bytes) = Vec::<F>::read_transcription_bytes_subset(bytes);
47 let (down_evals, bytes) = Vec::<F>::read_transcription_bytes_subset(bytes);
48 let (bit_op_evals, bytes) = Vec::<F>::read_transcription_bytes_subset(bytes);
49 assert!(bytes.is_empty(), "All bytes should be consumed");
50 Self {
51 up_evals,
52 down_evals,
53 bit_op_evals,
54 }
55 }
56
57 fn write_transcription_bytes_exact(&self, buf: &mut [u8]) {
58 let buf = self.up_evals.write_transcription_bytes_subset(buf);
59 let buf = self.down_evals.write_transcription_bytes_subset(buf);
60 let buf = self.bit_op_evals.write_transcription_bytes_subset(buf);
61 assert!(buf.is_empty(), "Entire buffer should be used");
62 }
63}
64
65impl<F: ConstTranscribable> Transcribable for Proof<F> {
66 fn get_num_bytes(&self) -> usize {
67 add!(
68 3 * u32::NUM_BYTES,
69 add!(
70 self.up_evals.get_num_bytes(),
71 add!(
72 self.down_evals.get_num_bytes(),
73 self.bit_op_evals.get_num_bytes()
74 )
75 )
76 )
77 }
78}
79
80impl<F: SetElement> Proof<F> {
81 /// Check that the proof's evaluation vectors have the expected lengths.
82 pub fn validate_evaluation_sizes(
83 &self,
84 num_cols: usize,
85 num_down_cols: usize,
86 num_bit_op_specs: usize,
87 ) -> Result<(), CombinedPolyResolverError<F>> {
88 if self.up_evals.len() != num_cols {
89 return Err(CombinedPolyResolverError::WrongUpEvalsNumber {
90 got: self.up_evals.len(),
91 expected: num_cols,
92 });
93 }
94
95 if self.down_evals.len() != num_down_cols {
96 return Err(CombinedPolyResolverError::WrongDownEvalsNumber {
97 got: self.down_evals.len(),
98 expected: num_down_cols,
99 });
100 }
101
102 if self.bit_op_evals.len() != num_bit_op_specs {
103 return Err(CombinedPolyResolverError::WrongBitOpEvalsNumber {
104 got: self.bit_op_evals.len(),
105 expected: num_bit_op_specs,
106 });
107 }
108
109 Ok(())
110 }
111}
112
113pub struct ProverState<F> {
114 /// The shared evaluation point yielded by the multi-degree sumcheck.
115 pub evaluation_point: Vec<F>,
116}
117
118/// Ancillary data produced by `prepare_sumcheck_group` and consumed by
119/// `finalize_prover`. Holds everything needed to extract `up_evals` /
120/// `down_evals` / `bit_op_evals` after the shared sumcheck completes.
121pub struct CprProverAncillary {
122 /// Number of trace (up) columns — used to split the flat evals vec.
123 pub num_cols: usize,
124 /// Number of shift-virtual (down) columns.
125 pub num_down_cols: usize,
126 /// Number of variables — used to index the last challenge.
127 pub num_vars: usize,
128}
129
130/// Ancillary data produced by `prepare_verifier` and consumed by
131/// `finalize_verifier`. Holds state that bridges the pre-sumcheck and
132/// post-sumcheck halves of the CPR verifier.
133pub struct CprVerifierAncillary<F> {
134 /// Powers of the folding challenge α: [1, α, α², ..., α^{k-1}].
135 pub folding_challenge_powers: Vec<F>,
136 /// Evaluation point from the ideal check subclaim (for eq_r computation).
137 pub ic_evaluation_point: Vec<F>,
138 /// Number of variables (for selector computation).
139 pub num_vars: usize,
140}
141
142/// The claim that is left to be proven after the combined polynomial resolver
143/// verifier has succeeded. It is a list of evaluation claims at a common
144/// evaluation point, covering committed trace columns, row-shift virtual
145/// columns, and bit-op virtual columns. Bit-op evals are kept separate so the
146/// downstream `MultipointEval` can bind them back to the source columns'
147/// lifted openings (per Lemma 2.3 of the Zinc+ paper) rather than treating
148/// them as standalone trusted evaluations.
149#[derive(Clone, Debug)]
150pub struct VerifierSubclaim<F> {
151 /// Evaluation point for the claims.
152 pub evaluation_point: Vec<F>,
153 /// Evaluation claims about the trace columns.
154 pub up_evals: Vec<F>,
155 /// Evaluation claims about the row-shift virtual columns.
156 pub down_evals: Vec<F>,
157 /// Evaluation claims about the bit-op virtual columns, in
158 /// `UairSignature::bit_op_specs()` order.
159 pub bit_op_evals: Vec<F>,
160}