winter_prover/trace/trace_lde/default/
mod.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::vec::Vec;
7use core::marker::PhantomData;
8
9use air::{proof::Queries, PartitionOptions, TraceInfo};
10use crypto::VectorCommitment;
11use tracing::info_span;
12
13use super::{
14    ColMatrix, ElementHasher, EvaluationFrame, FieldElement, StarkDomain, TraceLde, TracePolyTable,
15};
16use crate::{RowMatrix, DEFAULT_SEGMENT_WIDTH};
17
18#[cfg(test)]
19mod tests;
20
21// TRACE LOW DEGREE EXTENSION
22// ================================================================================================
23/// Contains all segments of the extended execution trace, the commitments to these segments, the
24/// LDE blowup factor, and the [TraceInfo].
25///
26/// Segments are stored in two groups:
27/// - Main segment: this is the first trace segment generated by the prover. Values in this segment
28///   will always be elements in the base field (even when an extension field is used).
29/// - Auxiliary segments: a list of 0 or more segments for traces generated after the prover commits
30///   to the first trace segment. Currently, at most 1 auxiliary segment is possible.
31pub struct DefaultTraceLde<
32    E: FieldElement,
33    H: ElementHasher<BaseField = E::BaseField>,
34    V: VectorCommitment<H>,
35> {
36    // low-degree extension of the main segment of the trace
37    main_segment_lde: RowMatrix<E::BaseField>,
38    // commitment to the main segment of the trace
39    main_segment_oracles: V,
40    // low-degree extensions of the auxiliary segment of the trace
41    aux_segment_lde: Option<RowMatrix<E>>,
42    // commitment to the auxiliary segment of the trace
43    aux_segment_oracles: Option<V>,
44    blowup: usize,
45    trace_info: TraceInfo,
46    partition_options: PartitionOptions,
47    _h: PhantomData<H>,
48}
49
50impl<E, H, V> DefaultTraceLde<E, H, V>
51where
52    E: FieldElement,
53    H: ElementHasher<BaseField = E::BaseField>,
54    V: VectorCommitment<H>,
55{
56    /// Takes the main trace segment columns as input, interpolates them into polynomials in
57    /// coefficient form, evaluates the polynomials over the LDE domain, commits to the
58    /// polynomial evaluations, and creates a new [DefaultTraceLde] with the LDE of the main trace
59    /// segment and the commitment.
60    ///
61    /// Returns a tuple containing a [TracePolyTable] with the trace polynomials for the main trace
62    /// segment and the new [DefaultTraceLde].
63    pub fn new(
64        trace_info: &TraceInfo,
65        main_trace: &ColMatrix<E::BaseField>,
66        domain: &StarkDomain<E::BaseField>,
67        partition_options: PartitionOptions,
68    ) -> (Self, TracePolyTable<E>) {
69        // extend the main execution trace and build a commitment to the extended trace
70        let (main_segment_lde, main_segment_vector_com, main_segment_polys) =
71            build_trace_commitment::<E, E::BaseField, H, V>(main_trace, domain, partition_options);
72
73        let trace_poly_table = TracePolyTable::new(main_segment_polys);
74        let trace_lde = DefaultTraceLde {
75            main_segment_lde,
76            main_segment_oracles: main_segment_vector_com,
77            aux_segment_lde: None,
78            aux_segment_oracles: None,
79            blowup: domain.trace_to_lde_blowup(),
80            trace_info: trace_info.clone(),
81            partition_options,
82            _h: PhantomData,
83        };
84
85        (trace_lde, trace_poly_table)
86    }
87
88    // TEST HELPERS
89    // --------------------------------------------------------------------------------------------
90
91    /// Returns number of columns in the main segment of the execution trace.
92    #[cfg(test)]
93    pub fn main_segment_width(&self) -> usize {
94        self.main_segment_lde.num_cols()
95    }
96
97    /// Returns a reference to [Matrix] representing the main trace segment.
98    #[cfg(test)]
99    pub fn get_main_segment(&self) -> &RowMatrix<E::BaseField> {
100        &self.main_segment_lde
101    }
102
103    /// Returns the entire trace for the column at the specified index.
104    #[cfg(test)]
105    pub fn get_main_segment_column(&self, col_idx: usize) -> Vec<E::BaseField> {
106        (0..self.main_segment_lde.num_rows())
107            .map(|row_idx| self.main_segment_lde.get(col_idx, row_idx))
108            .collect()
109    }
110}
111
112impl<E, H, V> TraceLde<E> for DefaultTraceLde<E, H, V>
113where
114    E: FieldElement,
115    H: ElementHasher<BaseField = E::BaseField> + core::marker::Sync,
116    V: VectorCommitment<H> + core::marker::Sync,
117{
118    type HashFn = H;
119    type VC = V;
120
121    /// Returns the commitment to the low-degree extension of the main trace segment.
122    fn get_main_trace_commitment(&self) -> H::Digest {
123        self.main_segment_oracles.commitment()
124    }
125
126    /// Takes auxiliary trace segment columns as input, interpolates them into polynomials in
127    /// coefficient form, evaluates the polynomials over the LDE domain, and commits to the
128    /// polynomial evaluations. Depending on whether `num_partitions` is equal to `1` or is
129    /// greater than `1`, committing to the polynomial evaluations row-wise is done either
130    /// in one go in the former or in `num_partition` steps which are then combined in the latter.
131    ///
132    /// Returns a tuple containing the column polynomials in coefficient from and the commitment
133    /// to the polynomial evaluations over the LDE domain.
134    ///
135    /// # Panics
136    ///
137    /// This function will panic if any of the following are true:
138    /// - the number of rows in the provided `aux_trace` does not match the main trace.
139    /// - the auxiliary trace has been previously set already.
140    fn set_aux_trace(
141        &mut self,
142        aux_trace: &ColMatrix<E>,
143        domain: &StarkDomain<E::BaseField>,
144    ) -> (ColMatrix<E>, H::Digest) {
145        // extend the auxiliary trace segment and build a commitment to the extended trace
146        let (aux_segment_lde, aux_segment_oracles, aux_segment_polys) =
147            build_trace_commitment::<E, E, H, Self::VC>(aux_trace, domain, self.partition_options);
148
149        // check errors
150        assert!(
151            usize::from(self.aux_segment_lde.is_some()) < self.trace_info.num_aux_segments(),
152            "the auxiliary trace has already been added"
153        );
154        assert_eq!(
155            self.main_segment_lde.num_rows(),
156            aux_segment_lde.num_rows(),
157            "the number of rows in the auxiliary segment must be the same as in the main segment"
158        );
159
160        // save the lde and commitment
161        self.aux_segment_lde = Some(aux_segment_lde);
162        let commitment_string = aux_segment_oracles.commitment();
163        self.aux_segment_oracles = Some(aux_segment_oracles);
164
165        (aux_segment_polys, commitment_string)
166    }
167
168    /// Reads current and next rows from the main trace segment into the specified frame.
169    fn read_main_trace_frame_into(
170        &self,
171        lde_step: usize,
172        frame: &mut EvaluationFrame<E::BaseField>,
173    ) {
174        // at the end of the trace, next state wraps around and we read the first step again
175        let next_lde_step = (lde_step + self.blowup()) % self.trace_len();
176
177        // copy main trace segment values into the frame
178        frame.current_mut().copy_from_slice(self.main_segment_lde.row(lde_step));
179        frame.next_mut().copy_from_slice(self.main_segment_lde.row(next_lde_step));
180    }
181
182    /// Reads current and next rows from the auxiliary trace segment into the specified frame.
183    ///
184    /// # Panics
185    /// This currently assumes that there is exactly one auxiliary trace segment, and will panic
186    /// otherwise.
187    fn read_aux_trace_frame_into(&self, lde_step: usize, frame: &mut EvaluationFrame<E>) {
188        // at the end of the trace, next state wraps around and we read the first step again
189        let next_lde_step = (lde_step + self.blowup()) % self.trace_len();
190
191        // copy auxiliary trace segment values into the frame
192        let segment = self.aux_segment_lde.as_ref().expect("expected aux segment to be present");
193        frame.current_mut().copy_from_slice(segment.row(lde_step));
194        frame.next_mut().copy_from_slice(segment.row(next_lde_step));
195    }
196
197    /// Returns trace table rows at the specified positions along with an opening proof to these
198    /// rows againt the already computed commitment.
199    fn query(&self, positions: &[usize]) -> Vec<Queries> {
200        // build queries for the main trace segment
201        let mut result = vec![build_segment_queries::<E::BaseField, H, V>(
202            &self.main_segment_lde,
203            &self.main_segment_oracles,
204            positions,
205        )];
206
207        // build queries for the auxiliary trace segment
208        if let Some(ref segment_oracles) = self.aux_segment_oracles {
209            let segment_lde =
210                self.aux_segment_lde.as_ref().expect("expected aux segment to be present");
211            result.push(build_segment_queries::<E, H, V>(segment_lde, segment_oracles, positions));
212        }
213
214        result
215    }
216
217    /// Returns the number of rows in the execution trace.
218    fn trace_len(&self) -> usize {
219        self.main_segment_lde.num_rows()
220    }
221
222    /// Returns blowup factor which was used to extend original execution trace into trace LDE.
223    fn blowup(&self) -> usize {
224        self.blowup
225    }
226
227    /// Returns the trace info of the execution trace.
228    fn trace_info(&self) -> &TraceInfo {
229        &self.trace_info
230    }
231}
232
233// HELPER FUNCTIONS
234// ================================================================================================
235
236/// Computes a low-degree extension (LDE) of the provided execution trace over the specified
237/// domain and builds a commitment to the extended trace.
238///
239/// The extension is performed by interpolating each column of the execution trace into a
240/// polynomial of degree = trace_length - 1, and then evaluating the polynomial over the LDE
241/// domain.
242///
243/// The trace commitment is computed by building a vector containing the hashes of each row of
244/// the extended execution trace, then building a vector commitment to the resulting vector.
245fn build_trace_commitment<E, F, H, V>(
246    trace: &ColMatrix<F>,
247    domain: &StarkDomain<E::BaseField>,
248    partition_options: PartitionOptions,
249) -> (RowMatrix<F>, V, ColMatrix<F>)
250where
251    E: FieldElement,
252    F: FieldElement<BaseField = E::BaseField>,
253    H: ElementHasher<BaseField = E::BaseField>,
254    V: VectorCommitment<H>,
255{
256    // extend the execution trace
257    let (trace_lde, trace_polys) = {
258        let span = info_span!(
259            "extend_execution_trace",
260            num_cols = trace.num_cols(),
261            blowup = domain.trace_to_lde_blowup()
262        )
263        .entered();
264        let trace_polys = trace.interpolate_columns();
265        let trace_lde =
266            RowMatrix::evaluate_polys_over::<DEFAULT_SEGMENT_WIDTH>(&trace_polys, domain);
267        drop(span);
268
269        (trace_lde, trace_polys)
270    };
271    assert_eq!(trace_lde.num_cols(), trace.num_cols());
272    assert_eq!(trace_polys.num_rows(), trace.num_rows());
273    assert_eq!(trace_lde.num_rows(), domain.lde_domain_size());
274
275    // build trace commitment
276    let commitment_domain_size = trace_lde.num_rows();
277    let trace_vector_com = info_span!("compute_execution_trace_commitment", commitment_domain_size)
278        .in_scope(|| trace_lde.commit_to_rows::<H, V>(partition_options));
279    assert_eq!(trace_vector_com.domain_len(), commitment_domain_size);
280
281    (trace_lde, trace_vector_com, trace_polys)
282}
283
284fn build_segment_queries<E, H, V>(
285    segment_lde: &RowMatrix<E>,
286    segment_vector_com: &V,
287    positions: &[usize],
288) -> Queries
289where
290    E: FieldElement,
291    H: ElementHasher<BaseField = E::BaseField>,
292    V: VectorCommitment<H>,
293{
294    // for each position, get the corresponding row from the trace segment LDE and put all these
295    // rows into a single vector
296    let trace_states =
297        positions.iter().map(|&pos| segment_lde.row(pos).to_vec()).collect::<Vec<_>>();
298
299    // build a batch opening proof to the leaves specified by positions
300    let trace_proof = segment_vector_com
301        .open_many(positions)
302        .expect("failed to generate a batch opening proof for trace queries");
303
304    Queries::new::<H, E, V>(trace_proof.1, trace_states)
305}