Skip to main content

dory_pcs/
proof.rs

1//! Dory proof structure
2//!
3//! A Dory proof consists of:
4//! - VMV message (PCS transform)
5//! - Multiple rounds of reduce messages (log n rounds)
6//! - Final scalar product message (transparent) or Σ-proofs (ZK)
7
8use crate::error::DoryError;
9use crate::messages::*;
10use crate::primitives::arithmetic::Group;
11use std::marker::PhantomData;
12
13/// A complete Dory evaluation proof
14///
15/// The proof demonstrates that a committed polynomial evaluates to a specific value
16/// at a given point. It consists of messages from the interactive protocol made
17/// non-interactive via Fiat-Shamir.
18///
19/// The proof includes the matrix dimensions (nu, sigma) used during proof generation,
20/// which the verifier uses to ensure consistency with the evaluation point.
21#[derive(Clone, Debug, PartialEq)]
22#[allow(missing_docs)]
23pub struct DoryProof<G1: Group, G2, GT> {
24    /// Vector-Matrix-Vector message for PCS transformation
25    pub vmv_message: VMVMessage<G1, GT>,
26
27    /// First reduce messages for each round (nu rounds total)
28    pub first_messages: Vec<FirstReduceMessage<G1, G2, GT>>,
29
30    /// Second reduce messages for each round (nu rounds total)
31    pub second_messages: Vec<SecondReduceMessage<G1, G2, GT>>,
32
33    /// Final scalar product message revealing the folded witness.
34    ///
35    /// `Some` in transparent mode. `None` in ZK mode, where revealing the
36    /// folded witness would break hiding: the scalar-product Σ-proof
37    /// (`scalar_product_proof`) replaces it.
38    pub final_message: Option<ScalarProductMessage<G1, G2>>,
39
40    /// Log₂ of number of rows in the coefficient matrix
41    pub nu: usize,
42
43    /// Log₂ of number of columns in the coefficient matrix
44    pub sigma: usize,
45
46    /// Blinded E₂ element for zero-knowledge proofs
47    #[cfg(feature = "zk")]
48    pub e2: Option<G2>,
49    /// Pedersen commitment to the blinding vector y
50    #[cfg(feature = "zk")]
51    pub y_com: Option<G1>,
52    /// Σ₁ proof: E₂ and y_com commit to the same y
53    #[cfg(feature = "zk")]
54    pub sigma1_proof: Option<Sigma1Proof<G1, G2, G1::Scalar>>,
55    /// Σ₂ proof: consistency of E₁ with D₂
56    #[cfg(feature = "zk")]
57    pub sigma2_proof: Option<Sigma2Proof<G1::Scalar, GT>>,
58    /// ZK scalar product proof: (C, D₁, D₂) consistency with blinded vectors
59    #[cfg(feature = "zk")]
60    pub scalar_product_proof: Option<ScalarProductProof<G1, G2, G1::Scalar, GT>>,
61}
62
63/// A [`DoryProof`] classified by mode, carrying references to the fields that
64/// mode guarantees (see [`DoryProof::mode`]).
65#[derive(Clone, Copy)]
66pub enum ProofMode<'a, G1: Group, G2, GT> {
67    /// Transparent proof: reveals the folded witness as the clear final
68    /// message and carries no ZK fields. (The phantom borrow ties down `GT`,
69    /// which only the ZK variant otherwise uses, keeping the enum's generics —
70    /// and auto traits — identical across feature flags.)
71    Transparent(&'a ScalarProductMessage<G1, G2>, PhantomData<&'a GT>),
72    /// ZK proof: carries every blinding field and Σ-proof
73    #[cfg(feature = "zk")]
74    Zk {
75        /// Blinded E₂ from the VMV message.
76        e2: &'a G2,
77        /// Pedersen commitment to the claimed evaluation.
78        y_com: &'a G1,
79        /// Σ₁ proof: E₂ and y_com commit to the same y.
80        sigma1: &'a Sigma1Proof<G1, G2, G1::Scalar>,
81        /// Σ₂ proof: VMV constraint (batched into the final check).
82        sigma2: &'a Sigma2Proof<G1::Scalar, GT>,
83        /// Scalar-product Σ-proof over the folded statement.
84        scalar_product: &'a ScalarProductProof<G1, G2, G1::Scalar, GT>,
85    },
86}
87
88impl<G1: Group, G2, GT> DoryProof<G1, G2, GT> {
89    /// Classify this proof by shape, rejecting mix-and-match proofs.
90    ///
91    /// A proof must be *fully* transparent — clear final message present, no
92    /// ZK fields — or *fully* ZK — every ZK field present, no clear final
93    /// message. Anything in between is malformed: extra fields would either be
94    /// ignored by verification (making the proof bytes malleable — two
95    /// distinct serialized proofs for one statement) or reveal data a ZK proof
96    /// must hide.
97    ///
98    /// This is the single enforcement point of that invariant;
99    /// `verify_evaluation_proof` calls it before reading any optional field,
100    /// and the returned [`ProofMode`] hands out references to exactly the
101    /// fields the shape guarantees.
102    ///
103    /// # Errors
104    /// Returns [`DoryError::InvalidProof`] for any mixed or incomplete shape.
105    pub fn mode(&self) -> Result<ProofMode<'_, G1, G2, GT>, DoryError> {
106        #[cfg(feature = "zk")]
107        {
108            if let (Some(e2), Some(y_com), Some(sigma1), Some(sigma2), Some(scalar_product)) = (
109                &self.e2,
110                &self.y_com,
111                &self.sigma1_proof,
112                &self.sigma2_proof,
113                &self.scalar_product_proof,
114            ) {
115                return if self.final_message.is_none() {
116                    Ok(ProofMode::Zk {
117                        e2,
118                        y_com,
119                        sigma1,
120                        sigma2,
121                        scalar_product,
122                    })
123                } else {
124                    Err(DoryError::InvalidProof)
125                };
126            }
127            // Not fully ZK: every ZK field must then be absent.
128            if self.e2.is_some()
129                || self.y_com.is_some()
130                || self.sigma1_proof.is_some()
131                || self.sigma2_proof.is_some()
132                || self.scalar_product_proof.is_some()
133            {
134                return Err(DoryError::InvalidProof);
135            }
136        }
137        self.final_message
138            .as_ref()
139            .map(|msg| ProofMode::Transparent(msg, PhantomData))
140            .ok_or(DoryError::InvalidProof)
141    }
142}