Skip to main content

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}