Skip to main content

slop_stacked/
verifier.rs

1use derive_where::derive_where;
2use itertools::Itertools;
3use slop_algebra::TwoAdicField;
4use slop_basefold::{BaseFoldVerifierError, BasefoldProof, BasefoldVerifier};
5use slop_challenger::IopCtx;
6use slop_commit::Rounds;
7use slop_merkle_tree::MerkleTreeTcsError;
8use slop_multilinear::{Mle, MleEval, MultilinearPcsVerifier, Point};
9use thiserror::Error;
10#[derive(Clone, Debug)]
11pub struct StackedPcsVerifier<GC: IopCtx> {
12    pub basefold_verifier: BasefoldVerifier<GC>,
13    pub log_stacking_height: u32,
14    _marker: std::marker::PhantomData<GC>,
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
18pub enum StackedVerifierError<PcsError> {
19    #[error("PCS error: {0}")]
20    PcsError(PcsError),
21    #[error("Batch evaluations do not match the claimed evaluations")]
22    StackingError,
23    #[error("Proof has incorrect shape")]
24    IncorrectShape,
25}
26
27#[derive_where(Debug, Clone, Serialize, Deserialize; MleEval<GC::EF>, BasefoldProof<GC>)]
28pub struct StackedBasefoldProof<GC: IopCtx> {
29    pub basefold_proof: BasefoldProof<GC>,
30    pub batch_evaluations: Rounds<MleEval<GC::EF>>,
31}
32
33impl<GC: IopCtx> StackedPcsVerifier<GC> {
34    #[inline]
35    pub const fn new(basefold_verifier: BasefoldVerifier<GC>, log_stacking_height: u32) -> Self {
36        Self { basefold_verifier, log_stacking_height, _marker: std::marker::PhantomData }
37    }
38
39    pub fn verify_trusted_evaluation(
40        &self,
41        commitments: &[GC::Digest],
42        round_areas: &[usize],
43        point: &Point<GC::EF>,
44        proof: &StackedBasefoldProof<GC>,
45        evaluation_claim: GC::EF,
46        challenger: &mut GC::Challenger,
47    ) -> Result<(), StackedVerifierError<BaseFoldVerifierError<MerkleTreeTcsError>>>
48    where
49        GC::F: TwoAdicField,
50    {
51        if point.dimension() < self.log_stacking_height as usize {
52            return Err(StackedVerifierError::IncorrectShape);
53        }
54
55        // Split the point into the interleaved and batched parts.
56        let (batch_point, stack_point) =
57            point.split_at(point.dimension() - self.log_stacking_height as usize);
58
59        if proof.batch_evaluations.len() != round_areas.len()
60            || commitments.len() != round_areas.len()
61        {
62            return Err(StackedVerifierError::IncorrectShape);
63        }
64
65        for (round_area, proof_evaluation_len) in
66            round_areas.iter().zip_eq(proof.batch_evaluations.iter())
67        {
68            if !round_area.is_multiple_of(1 << self.log_stacking_height)
69                || round_area >> self.log_stacking_height as usize
70                    != proof_evaluation_len.num_polynomials()
71            {
72                return Err(StackedVerifierError::IncorrectShape);
73            }
74        }
75
76        // Interpolate the batch evaluations as a multilinear polynomial.
77        let batch_evaluations =
78            proof.batch_evaluations.iter().flatten().cloned().collect::<Mle<_>>();
79
80        // Verify that the climed evaluations matched the interpolated evaluations.
81        let expected_evaluation = batch_evaluations.blocking_eval_at(&batch_point)[0];
82        if evaluation_claim != expected_evaluation {
83            return Err(StackedVerifierError::StackingError);
84        }
85
86        // Verify the PCS proof with respect to the claimed evaluations.
87        // It is assumed that the multilinear batch PCS verifier checks that the number of
88        // commitments is as expected.
89        self.basefold_verifier
90            .verify_untrusted_evaluations(
91                commitments,
92                stack_point,
93                &proof.batch_evaluations,
94                &proof.basefold_proof,
95                challenger,
96            )
97            .map_err(StackedVerifierError::PcsError)
98    }
99}
100
101impl<GC: IopCtx> MultilinearPcsVerifier<GC> for StackedPcsVerifier<GC>
102where
103    GC::F: TwoAdicField,
104{
105    type VerifierError = StackedVerifierError<BaseFoldVerifierError<MerkleTreeTcsError>>;
106
107    type Proof = StackedBasefoldProof<GC>;
108
109    fn num_expected_commitments(&self) -> usize {
110        self.basefold_verifier.num_expected_commitments
111    }
112    fn verify_trusted_evaluation(
113        &self,
114        commitments: &[<GC as IopCtx>::Digest],
115        round_polynomial_sizes: &[usize],
116        point: Point<<GC as IopCtx>::EF>,
117        evaluation_claim: <GC as IopCtx>::EF,
118        proof: &Self::Proof,
119        challenger: &mut <GC as IopCtx>::Challenger,
120    ) -> Result<(), Self::VerifierError> {
121        self.verify_trusted_evaluation(
122            commitments,
123            round_polynomial_sizes,
124            &point,
125            proof,
126            evaluation_claim,
127            challenger,
128        )
129    }
130
131    /// The jagged verifier will assume that the underlying PCS will pad commitments to a multiple
132    /// of `1<<log.stacking_height(verifier)`.
133    fn log_stacking_height(verifier: &Self) -> u32 {
134        verifier.log_stacking_height
135    }
136}