winter_fri/proof.rs
1// Copyright (c) Facebook, Inc. and its affiliates.
2//
3// This source code is licensed under the MIT license found in the
4// LICENSE file in the root directory of this source tree.
5
6use alloc::{string::ToString, vec::Vec};
7
8use crypto::{ElementHasher, Hasher, VectorCommitment};
9use math::FieldElement;
10use utils::{
11 ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable, SliceReader,
12};
13
14// FRI PROOF
15// ================================================================================================
16
17/// A proof generated by a FRI prover.
18///
19/// A FRI proof contains information proving that a function *f* is a polynomial of some bounded
20/// degree *d*. FRI proofs cannot be instantiated directly - they must be generated by an instance
21/// of a [FriProver](crate::FriProver), and can be verified by an instance of a
22/// [FriVerifier](crate::FriVerifier) via [VerifierChannel](crate::VerifierChannel) interface.
23///
24/// A proof consists of zero or more layers and a remainder polynomial. Each layer contains a set of
25/// polynomial evaluations at positions queried by the verifier, a vector commitment to LDE of
26/// each polynomial, as well as opening proofs for the evaluations against the vector commitments.
27/// The remainder polynomial is given by its list of coefficients i.e. field elements.
28///
29/// All values in a proof are stored as vectors of bytes. Thus, the values must be parsed before
30/// they can be returned to the user. To do this, [parse_layers()](FriProof::parse_layers())
31/// and [parse_remainder()](FriProof::parse_remainder()) methods can be used.
32#[derive(Debug, Clone, Eq, PartialEq)]
33pub struct FriProof {
34 layers: Vec<FriProofLayer>,
35 remainder: Vec<u8>,
36 num_partitions: u8, // stored as power of 2
37}
38
39impl FriProof {
40 // CONSTRUCTOR
41 // --------------------------------------------------------------------------------------------
42 /// Creates a new FRI proof from the provided layers and remainder polynomial.
43 ///
44 /// # Panics
45 /// Panics if:
46 /// * Number of remainder elements zero or is not a power of two.
47 /// * `num_partitions` is zero or is not a power of two.
48 pub(crate) fn new<E: FieldElement>(
49 layers: Vec<FriProofLayer>,
50 remainder: Vec<E>,
51 num_partitions: usize,
52 ) -> Self {
53 assert!(!remainder.is_empty(), "number of remainder elements must be greater than zero");
54 assert!(
55 remainder.len().is_power_of_two(),
56 "size of the remainder must be a power of two, but was {}",
57 remainder.len()
58 );
59 assert!(num_partitions > 0, "number of partitions must be greater than zero");
60 assert!(
61 num_partitions.is_power_of_two(),
62 "number of partitions must be a power of two, but was {num_partitions}"
63 );
64
65 let mut remainder_bytes = Vec::with_capacity(E::ELEMENT_BYTES * remainder.len());
66 remainder_bytes.write_many(&remainder);
67
68 FriProof {
69 layers,
70 remainder: remainder_bytes,
71 num_partitions: num_partitions.trailing_zeros() as u8,
72 }
73 }
74
75 /// Creates a dummy `FriProof` for use in tests.
76 pub fn new_dummy() -> Self {
77 Self {
78 layers: Vec::new(),
79 remainder: Vec::new(),
80 num_partitions: 0,
81 }
82 }
83
84 // PUBLIC ACCESSORS
85 // --------------------------------------------------------------------------------------------
86
87 /// Returns the number of layers in this proof.
88 pub fn num_layers(&self) -> usize {
89 self.layers.len()
90 }
91
92 /// Returns the number of remainder elements in this proof.
93 ///
94 /// The number of elements is computed by dividing the number of remainder bytes by the size
95 /// of the field element specified by `E` type parameter.
96 pub fn num_remainder_elements<E: FieldElement>(&self) -> usize {
97 self.remainder.len() / E::ELEMENT_BYTES
98 }
99
100 /// Returns the number of partitions used during proof generation.
101 pub fn num_partitions(&self) -> usize {
102 2usize.pow(self.num_partitions as u32)
103 }
104
105 /// Returns the size of this proof in bytes.
106 pub fn size(&self) -> usize {
107 // +1 for number of layers, +1 for remainder length, +1 for number of partitions
108 self.layers
109 .iter()
110 .fold(self.remainder.len() + 3, |acc, layer| acc + layer.size())
111 }
112
113 // PARSING
114 // --------------------------------------------------------------------------------------------
115
116 /// Decomposes this proof into vectors of query values for each layer and corresponding batch
117 /// opening proofs.
118 ///
119 /// # Panics
120 /// Panics if:
121 /// * `domain_size` is not a power of two.
122 /// * `folding_factor` is smaller than two or is not a power of two.
123 ///
124 /// # Errors
125 /// Returns an error if:
126 /// * This proof is not consistent with the specified `domain_size` and `folding_factor`.
127 /// * Any of the layers could not be parsed successfully.
128 #[allow(clippy::type_complexity)]
129 pub fn parse_layers<E, H, V>(
130 self,
131 mut domain_size: usize,
132 folding_factor: usize,
133 ) -> Result<(Vec<Vec<E>>, Vec<<V as VectorCommitment<H>>::MultiProof>), DeserializationError>
134 where
135 E: FieldElement,
136 H: ElementHasher<BaseField = E::BaseField>,
137 V: VectorCommitment<H>,
138 {
139 assert!(domain_size.is_power_of_two(), "domain size must be a power of two");
140 assert!(folding_factor.is_power_of_two(), "folding factor must be a power of two");
141 assert!(folding_factor > 1, "folding factor must be greater than 1");
142
143 let mut layer_proofs = Vec::new();
144 let mut layer_queries = Vec::new();
145
146 // parse all layers
147 for (i, layer) in self.layers.into_iter().enumerate() {
148 domain_size /= folding_factor;
149 let (qv, op) = layer.parse::<_, H, V>(folding_factor).map_err(|err| {
150 DeserializationError::InvalidValue(format!("failed to parse FRI layer {i}: {err}"))
151 })?;
152
153 // check that the opening proof matches the domain length
154 if <V as VectorCommitment<H>>::get_multiproof_domain_len(&op) != domain_size {
155 return Err(DeserializationError::InvalidValue(format!(
156 "expected a domain of size {} but was {}",
157 domain_size,
158 <V as VectorCommitment<H>>::get_multiproof_domain_len(&op),
159 )));
160 }
161
162 layer_proofs.push(op);
163 layer_queries.push(qv);
164 }
165
166 Ok((layer_queries, layer_proofs))
167 }
168
169 /// Returns a vector of remainder values (last FRI layer) parsed from this proof.
170 ///
171 /// # Errors
172 /// Returns an error if:
173 /// * The number of remainder values implied by a combination of `E` type parameter and the
174 /// number of remainder bytes in this proof is not a power of two.
175 /// * Any of the remainder values could not be parsed correctly.
176 /// * Not all bytes have been consumed while parsing remainder values.
177 pub fn parse_remainder<E: FieldElement>(&self) -> Result<Vec<E>, DeserializationError> {
178 let num_elements = self.num_remainder_elements::<E>();
179 if !num_elements.is_power_of_two() {
180 return Err(DeserializationError::InvalidValue(format!(
181 "number of remainder values must be a power of two, but {num_elements} was implied"
182 )));
183 }
184 let mut reader = SliceReader::new(&self.remainder);
185 let remainder = reader.read_many(num_elements).map_err(|err| {
186 DeserializationError::InvalidValue(format!("failed to parse FRI remainder: {err}"))
187 })?;
188 if reader.has_more_bytes() {
189 return Err(DeserializationError::UnconsumedBytes);
190 }
191 Ok(remainder)
192 }
193}
194
195// SERIALIZATION / DESERIALIZATION
196// ------------------------------------------------------------------------------------------------
197
198impl Serializable for FriProof {
199 /// Serializes `self` and writes the resulting bytes into the `target` writer.
200 fn write_into<W: ByteWriter>(&self, target: &mut W) {
201 // write layers
202 target.write_u8(self.layers.len() as u8);
203 for layer in self.layers.iter() {
204 layer.write_into(target);
205 }
206
207 // write remainder
208 target.write_u16(self.remainder.len() as u16);
209 target.write_bytes(&self.remainder);
210
211 // write number of partitions
212 target.write_u8(self.num_partitions);
213 }
214}
215
216impl Deserializable for FriProof {
217 /// Reads a FRI proof from the specified `source` and returns the result.
218 ///
219 /// # Errors
220 /// Returns an error if a valid proof could not be read from the source.
221 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
222 // read layers
223 let num_layers = source.read_u8()? as usize;
224 let layers = source.read_many(num_layers)?;
225
226 // read remainder
227 let num_remainder_bytes = source.read_u16()? as usize;
228 let remainder = source.read_vec(num_remainder_bytes)?;
229
230 // read number of partitions
231 let num_partitions = source.read_u8()?;
232
233 Ok(FriProof { layers, remainder, num_partitions })
234 }
235}
236
237// FRI PROOF LAYER
238// ================================================================================================
239
240#[derive(Debug, Clone, Eq, PartialEq)]
241pub struct FriProofLayer {
242 values: Vec<u8>,
243 paths: Vec<u8>,
244}
245
246impl FriProofLayer {
247 // CONSTRUCTOR
248 // --------------------------------------------------------------------------------------------
249 /// Creates a new proof layer from the specified query values and the corresponding batch
250 /// opening proof.
251 ///
252 /// # Panics
253 /// Panics if `query_values` is an empty slice.
254 pub(crate) fn new<E: FieldElement, H: Hasher, V: VectorCommitment<H>, const N: usize>(
255 query_values: Vec<[E; N]>,
256 proof: <V as VectorCommitment<H>>::MultiProof,
257 ) -> Self {
258 assert!(!query_values.is_empty(), "query values cannot be empty");
259
260 // TODO: add debug check that values actually hash into the leaf nodes of the batch proof
261
262 let mut value_bytes = Vec::with_capacity(E::ELEMENT_BYTES * N * query_values.len());
263 value_bytes.write_many(&query_values);
264
265 let mut proof_bytes = Vec::new();
266 proof.write_into(&mut proof_bytes);
267
268 FriProofLayer { values: value_bytes, paths: proof_bytes }
269 }
270
271 // PUBLIC ACCESSORS
272 // --------------------------------------------------------------------------------------------
273
274 /// Returns the size of this proof layer in bytes.
275 pub fn size(&self) -> usize {
276 // +4 for length of values, +4 for length of paths
277 self.values.len() + 4 + self.paths.len() + 4
278 }
279
280 // PARSING
281 // --------------------------------------------------------------------------------------------
282 /// Decomposes this layer into a combination of query values and corresponding batch opening
283 /// proof.
284 ///
285 /// # Errors
286 /// Returns an error if:
287 /// * This layer does not contain at least one query.
288 /// * Parsing of any of the query values or the corresponding batch opening proof fails.
289 /// * Not all bytes have been consumed while parsing this layer.
290 pub fn parse<E, H, V>(
291 self,
292 folding_factor: usize,
293 ) -> Result<(Vec<E>, <V as VectorCommitment<H>>::MultiProof), DeserializationError>
294 where
295 E: FieldElement,
296 H: ElementHasher<BaseField = E::BaseField>,
297 V: VectorCommitment<H>,
298 {
299 // make sure the number of value bytes can be parsed into a whole number of queries
300 let num_query_bytes = E::ELEMENT_BYTES * folding_factor;
301 if self.values.len() % num_query_bytes != 0 {
302 return Err(DeserializationError::InvalidValue(format!(
303 "number of value bytes ({}) does not divide into whole number of queries",
304 self.values.len(),
305 )));
306 }
307
308 let num_queries = self.values.len() / num_query_bytes;
309 if num_queries == 0 {
310 return Err(DeserializationError::InvalidValue(
311 "a FRI layer must contain at least one query".to_string(),
312 ));
313 }
314 let mut hashed_queries = vec![H::Digest::default(); num_queries];
315 let mut query_values = Vec::with_capacity(num_queries * folding_factor);
316
317 // read bytes corresponding to each query, convert them into field elements,
318 // and also hash them to build leaf nodes of the batch opening proof
319 let mut reader = SliceReader::new(&self.values);
320 for query_hash in hashed_queries.iter_mut() {
321 let mut qe = reader.read_many(folding_factor)?;
322 *query_hash = H::hash_elements(&qe);
323 query_values.append(&mut qe);
324 }
325 if reader.has_more_bytes() {
326 return Err(DeserializationError::UnconsumedBytes);
327 }
328
329 // build batch opening proof
330 let mut reader = SliceReader::new(&self.paths);
331 let multi_proof = <V::MultiProof as Deserializable>::read_from(&mut reader)?;
332 if reader.has_more_bytes() {
333 return Err(DeserializationError::UnconsumedBytes);
334 }
335
336 Ok((query_values, multi_proof))
337 }
338}
339
340// SERIALIZATION / DESERIALIZATION
341// ------------------------------------------------------------------------------------------------
342
343impl Serializable for FriProofLayer {
344 /// Serializes this proof layer and writes the resulting bytes to the specified `target`.
345 fn write_into<W: ByteWriter>(&self, target: &mut W) {
346 // write value bytes
347 target.write_u32(self.values.len() as u32);
348 target.write_bytes(&self.values);
349
350 // write path bytes
351 target.write_u32(self.paths.len() as u32);
352 target.write_bytes(&self.paths);
353 }
354}
355
356impl Deserializable for FriProofLayer {
357 /// Reads a single proof layer form the `source` and returns it.
358 ///
359 /// # Errors
360 /// Returns an error if a valid layer could not be read from the specified source.
361 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
362 // read values
363 let num_value_bytes = source.read_u32()?;
364 if num_value_bytes == 0 {
365 return Err(DeserializationError::InvalidValue(
366 "a FRI proof layer must contain at least one queried evaluation".to_string(),
367 ));
368 }
369 let values = source.read_vec(num_value_bytes as usize)?;
370
371 // read paths
372 let num_paths_bytes = source.read_u32()?;
373 let paths = source.read_vec(num_paths_bytes as usize)?;
374
375 Ok(FriProofLayer { values, paths })
376 }
377}