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}