winter_air/air/
trace_info.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 math::{StarkField, ToElements};
9use utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
10
11// CONSTANTS
12// ================================================================================================
13
14// TRACE INFO
15// ================================================================================================
16/// Information about a specific execution trace.
17///
18/// Trace info consists of the number of columns for all trace segments, trace length, the number of
19/// random elements needed to generate the auxiliary segment and optional custom metadata.
20///
21/// Currently, a trace can consist of at most two segments: the main segment and one auxiliary
22/// segment. Metadata is just a vector of bytes and can store any values up to 64KB in size.
23#[derive(Debug, Clone, Eq, PartialEq)]
24pub struct TraceInfo {
25    main_segment_width: usize,
26    aux_segment_width: usize,
27    num_aux_segment_rands: usize,
28    trace_length: usize,
29    trace_meta: Vec<u8>,
30}
31
32impl TraceInfo {
33    /// Smallest allowed execution trace length; currently set at 8.
34    pub const MIN_TRACE_LENGTH: usize = 8;
35    /// Maximum number of columns in an execution trace (across all segments); currently set at 255.
36    pub const MAX_TRACE_WIDTH: usize = 255;
37    /// Maximum number of bytes in trace metadata; currently set at 65535.
38    pub const MAX_META_LENGTH: usize = 65535;
39    /// Maximum number of random elements in the auxiliary trace segment; currently set to 255.
40    pub const MAX_RAND_SEGMENT_ELEMENTS: usize = 255;
41
42    // CONSTRUCTORS
43    // --------------------------------------------------------------------------------------------
44
45    /// Creates a new [TraceInfo] from the specified trace width and length.
46    ///
47    /// An execution trace described by this trace info is limited to a single segment.
48    ///
49    /// # Panics
50    /// Panics if:
51    /// * Trace width is zero or greater than 255.
52    /// * Trace length is smaller than 8 or is not a power of two.
53    pub fn new(width: usize, length: usize) -> Self {
54        Self::with_meta(width, length, vec![])
55    }
56
57    /// Creates a new [TraceInfo] from the specified trace width, length, and metadata.
58    ///
59    /// An execution trace described by this trace info is limited to a single segment.
60    ///
61    /// # Panics
62    /// Panics if:
63    /// * Trace width is zero or greater than 255.
64    /// * Trace length is smaller than 8 or is not a power of two.
65    /// * Length of `meta` is greater than 65535;
66    pub fn with_meta(width: usize, length: usize, meta: Vec<u8>) -> Self {
67        assert!(width > 0, "trace width must be greater than 0");
68        Self::new_multi_segment(width, 0, 0, length, meta)
69    }
70
71    /// Creates a new [TraceInfo] with main and auxiliary segments.
72    ///
73    /// # Panics
74    /// Panics if:
75    /// * The width of the first trace segment is zero.
76    /// * Total width of all trace segments is greater than 255.
77    /// * Trace length is smaller than 8 or is not a power of two.
78    /// * A zero entry in auxiliary segment width array is followed by a non-zero entry.
79    /// * Number of random elements for the auxiliary trace segment of non-zero width is set to
80    ///   zero.
81    /// * Number of random elements for the auxiliary trace segment of zero width is set to
82    ///   non-zero.
83    /// * Number of random elements for any auxiliary trace segment is greater than 255.
84    pub fn new_multi_segment(
85        main_segment_width: usize,
86        aux_segment_width: usize,
87        num_aux_segment_rands: usize,
88        trace_length: usize,
89        trace_meta: Vec<u8>,
90    ) -> Self {
91        assert!(
92            trace_length >= Self::MIN_TRACE_LENGTH,
93            "trace length must be at least {}, but was {}",
94            Self::MIN_TRACE_LENGTH,
95            trace_length
96        );
97        assert!(
98            trace_length.is_power_of_two(),
99            "trace length must be a power of two, but was {trace_length}"
100        );
101        assert!(
102            trace_meta.len() <= Self::MAX_META_LENGTH,
103            "number of metadata bytes cannot be greater than {}, but was {}",
104            Self::MAX_META_LENGTH,
105            trace_meta.len()
106        );
107
108        // validate trace segment widths
109        assert!(main_segment_width > 0, "main trace segment must consist of at least one column");
110        let full_width = main_segment_width + aux_segment_width;
111        assert!(
112            full_width <= TraceInfo::MAX_TRACE_WIDTH,
113            "total number of columns in the trace cannot be greater than {}, but was {}",
114            TraceInfo::MAX_TRACE_WIDTH,
115            full_width
116        );
117
118        // validate number of random elements required by the auxiliary segment
119        if aux_segment_width == 0 {
120            assert!(
121                num_aux_segment_rands == 0,
122                "number of random elements for an empty auxiliary trace segment must be zero"
123            );
124        }
125        assert!(
126            num_aux_segment_rands <= TraceInfo::MAX_RAND_SEGMENT_ELEMENTS,
127            "number of random elements required by a segment cannot exceed {}, but was {}",
128            TraceInfo::MAX_RAND_SEGMENT_ELEMENTS,
129            num_aux_segment_rands
130        );
131
132        TraceInfo {
133            main_segment_width,
134            aux_segment_width,
135            num_aux_segment_rands,
136            trace_length,
137            trace_meta,
138        }
139    }
140
141    // PUBLIC ACCESSORS
142    // --------------------------------------------------------------------------------------------
143
144    /// Returns the total number of columns in an execution trace.
145    ///
146    /// This is guaranteed to be between 1 and 255.
147    pub fn width(&self) -> usize {
148        self.main_segment_width + self.aux_segment_width
149    }
150
151    /// Returns execution trace length.
152    ///
153    /// The length is guaranteed to be a power of two.
154    pub fn length(&self) -> usize {
155        self.trace_length
156    }
157
158    /// Returns execution trace metadata.
159    pub fn meta(&self) -> &[u8] {
160        &self.trace_meta
161    }
162
163    /// Returns true if an execution trace contains the auxiliary trace segment.
164    pub fn is_multi_segment(&self) -> bool {
165        self.aux_segment_width > 0
166    }
167
168    /// Returns the number of columns in the main segment of an execution trace.
169    ///
170    /// This is guaranteed to be between 1 and 255.
171    pub fn main_trace_width(&self) -> usize {
172        self.main_segment_width
173    }
174
175    /// Returns the number of columns in the auxiliary segment of an execution trace.
176    pub fn aux_segment_width(&self) -> usize {
177        self.aux_segment_width
178    }
179
180    /// Returns the total number of segments in an execution trace.
181    pub fn num_segments(&self) -> usize {
182        if self.is_multi_segment() {
183            2
184        } else {
185            1
186        }
187    }
188
189    /// Returns the number of auxiliary trace segments in an execution trace.
190    pub fn num_aux_segments(&self) -> usize {
191        if self.is_multi_segment() {
192            1
193        } else {
194            0
195        }
196    }
197
198    /// Returns the number of columns in the auxiliary trace segment.
199    pub fn get_aux_segment_width(&self) -> usize {
200        self.aux_segment_width
201    }
202
203    /// Returns the number of random elements needed to build all auxiliary columns.
204    pub fn get_num_aux_segment_rand_elements(&self) -> usize {
205        self.num_aux_segment_rands
206    }
207}
208
209impl<E: StarkField> ToElements<E> for TraceInfo {
210    fn to_elements(&self) -> Vec<E> {
211        let mut result = Vec::new();
212
213        // main segment width, number of auxiliary segments, and parameters of the first auxiliary
214        // segment (if present) go into the first field element; we assume that each parameter can
215        // be encoded in 8 bits (which is enforced by the constructor)
216        let mut buf = self.main_segment_width as u32;
217        buf = (buf << 8) | self.num_aux_segments() as u32;
218        if self.num_aux_segments() == 1 {
219            buf = (buf << 8) | self.aux_segment_width as u32;
220            buf = (buf << 8) | self.num_aux_segment_rands as u32;
221        }
222        result.push(E::from(buf));
223
224        // We assume here that the trace length is never greater than 2^32.
225        result.push(E::from(self.trace_length as u32));
226
227        // convert trace metadata to elements; this is done by breaking trace metadata into chunks
228        // of bytes which are slightly smaller than the number of bytes needed to encode a field
229        // element, and then converting these chunks into field elements.
230        if !self.trace_meta.is_empty() {
231            for chunk in self.trace_meta.chunks(E::ELEMENT_BYTES - 1) {
232                result.push(E::from_bytes_with_padding(chunk));
233            }
234        }
235
236        result
237    }
238}
239
240impl Serializable for TraceInfo {
241    /// Serializes `self` and writes the resulting bytes into the `target`.
242    fn write_into<W: ByteWriter>(&self, target: &mut W) {
243        // store segments
244        target.write_u8(self.main_segment_width as u8);
245
246        debug_assert!(
247            self.aux_segment_width <= u8::MAX as usize,
248            "aux segment width does not fit into u8 value"
249        );
250        target.write_u8(self.aux_segment_width as u8);
251        debug_assert!(
252            self.num_aux_segment_rands <= u8::MAX as usize,
253            "aux segment random element count does not fit into u8 value"
254        );
255        target.write_u8(self.num_aux_segment_rands as u8);
256
257        // store trace length as power of two
258        target.write_u8(self.trace_length.ilog2() as u8);
259
260        // store trace meta
261        target.write_u16(self.trace_meta.len() as u16);
262        target.write_bytes(&self.trace_meta);
263    }
264}
265
266impl Deserializable for TraceInfo {
267    /// Reads [`TraceInfo`] from the specified `source` and returns the result.
268    ///
269    /// # Errors
270    /// Returns an error of a valid [`TraceInfo`] struct could not be read from the specified
271    /// `source`.
272    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
273        let main_segment_width = source.read_u8()? as usize;
274        if main_segment_width == 0 {
275            return Err(DeserializationError::InvalidValue(
276                "main trace segment width must be greater than zero".to_string(),
277            ));
278        }
279
280        // read auxiliary trace segment width
281        let aux_segment_width = source.read_u8()? as usize;
282
283        let full_trace_width = main_segment_width + aux_segment_width;
284        if full_trace_width >= TraceInfo::MAX_TRACE_WIDTH {
285            return Err(DeserializationError::InvalidValue(format!(
286                "full trace width cannot be greater than {}, but was {}",
287                TraceInfo::MAX_TRACE_WIDTH,
288                full_trace_width
289            )));
290        }
291
292        // read and validate number of random elements for the auxiliary trace segment
293        let num_aux_segment_rands = source.read_u8()? as usize;
294        if aux_segment_width != 0 && num_aux_segment_rands == 0 {
295            return Err(DeserializationError::InvalidValue(
296                "a non-empty trace segment must require at least one random element".to_string(),
297            ));
298        } else if num_aux_segment_rands > TraceInfo::MAX_RAND_SEGMENT_ELEMENTS {
299            return Err(DeserializationError::InvalidValue(format!(
300                "number of random elements required by a segment cannot exceed {}, but was {}",
301                TraceInfo::MAX_RAND_SEGMENT_ELEMENTS,
302                num_aux_segment_rands
303            )));
304        }
305
306        // read and validate trace length (which was stored as a power of two)
307        let trace_length = source.read_u8()?;
308        if trace_length < TraceInfo::MIN_TRACE_LENGTH.ilog2() as u8 {
309            return Err(DeserializationError::InvalidValue(format!(
310                "trace length cannot be smaller than 2^{}, but was 2^{}",
311                TraceInfo::MIN_TRACE_LENGTH.ilog2(),
312                trace_length
313            )));
314        }
315        let trace_length = 2_usize.pow(trace_length as u32);
316
317        // read trace metadata
318        let num_meta_bytes = source.read_u16()? as usize;
319        let trace_meta = if num_meta_bytes != 0 {
320            source.read_vec(num_meta_bytes)?
321        } else {
322            vec![]
323        };
324
325        Ok(Self::new_multi_segment(
326            main_segment_width,
327            aux_segment_width,
328            num_aux_segment_rands,
329            trace_length,
330            trace_meta,
331        ))
332    }
333}
334
335// TESTS
336// ================================================================================================
337
338#[cfg(test)]
339mod tests {
340    use math::{fields::f64::BaseElement, FieldElement};
341
342    use super::{ToElements, TraceInfo};
343
344    #[test]
345    fn trace_info_to_elements() {
346        // --- test trace with only main segment ------------------------------
347        let main_width = 20;
348        let trace_length = 64_u32;
349        let num_aux_segments = 0;
350
351        let expected = {
352            let first_ele = u32::from_le_bytes([num_aux_segments, main_width as u8, 0, 0]);
353
354            vec![BaseElement::from(first_ele), BaseElement::from(trace_length)]
355        };
356
357        let info = TraceInfo::new(main_width, trace_length as usize);
358        assert_eq!(expected, info.to_elements());
359
360        // --- test trace with one auxiliary segment --------------------------
361        let main_width = 20;
362        let trace_length = 64_u32;
363        let num_aux_segments = 1;
364        let aux_width = 9;
365        let aux_rands = 12;
366        let trace_meta = vec![1_u8, 2, 3, 4];
367
368        let expected = {
369            let first_ele =
370                u32::from_le_bytes([aux_rands as u8, aux_width, num_aux_segments, main_width]);
371
372            // `trace_meta` is 4 bytes, so fits into a single element
373            let mut meta_bytes = trace_meta.clone();
374            meta_bytes.resize(BaseElement::ELEMENT_BYTES, 0);
375            let meta_ele = BaseElement::try_from(meta_bytes.as_slice()).unwrap();
376
377            vec![BaseElement::from(first_ele), BaseElement::from(trace_length), meta_ele]
378        };
379
380        let info = TraceInfo::new_multi_segment(
381            main_width as usize,
382            aux_width as usize,
383            aux_rands,
384            trace_length as usize,
385            trace_meta,
386        );
387
388        assert_eq!(expected, info.to_elements());
389    }
390}