winter_air/air/assertions/
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::{
8    cmp::Ordering,
9    fmt::{Display, Formatter},
10};
11
12use math::FieldElement;
13
14use crate::errors::AssertionError;
15
16#[cfg(test)]
17mod tests;
18
19// CONSTANTS
20// ================================================================================================
21
22const MIN_STRIDE_LENGTH: usize = 2;
23const NO_STRIDE: usize = 0;
24
25// ASSERTION
26// ================================================================================================
27
28/// An assertion made against an execution trace.
29///
30/// An assertion is always placed against a single column of an execution trace, but can cover
31/// multiple steps and multiple values. Specifically, there are three kinds of assertions:
32///
33/// 1. **Single** assertion - which requires that a value in a single cell of an execution trace is
34///    equal to the specified value.
35/// 2. **Periodic** assertion - which requires that values in multiple cells of a single column are
36///    equal to the specified value. The cells must be evenly spaced at intervals with lengths equal
37///    to powers of two. For example, we can specify that values in a column must be equal to 0 at
38///    steps 0, 8, 16, 24, 32 etc. Steps can also start at some offset - e.g., 1, 9, 17, 25, 33 is
39///    also a valid sequence of steps.
40/// 3. **Sequence** assertion - which requires that multiple cells in a single column are equal to
41///    the values from the provided list. The cells must be evenly spaced at intervals with lengths
42///    equal to powers of two. For example, we can specify that values in a column must be equal to
43///    a sequence 1, 2, 3, 4 at steps 0, 8, 16, 24. That is, value at step 0 should be equal to 1,
44///    value at step 8 should be equal to 2 etc.
45///
46/// Note that single and periodic assertions are succinct. That is, a verifier can evaluate them
47/// very efficiently. However, sequence assertions have liner complexity in the number of
48/// asserted values. Though, unless many thousands of values are asserted, practical impact of
49/// this linear complexity should be negligible.
50#[derive(Debug, Clone, PartialEq, Eq)]
51pub struct Assertion<E: FieldElement> {
52    pub(super) column: usize,
53    pub(super) first_step: usize,
54    pub(super) stride: usize,
55    pub(super) values: Vec<E>,
56}
57
58impl<E: FieldElement> Assertion<E> {
59    // CONSTRUCTORS
60    // --------------------------------------------------------------------------------------------
61    /// Returns an assertion against a single cell of an execution trace.
62    ///
63    /// The returned assertion requires that the value in the specified `column` at the specified
64    /// `step` is equal to the provided `value`.
65    pub fn single(column: usize, step: usize, value: E) -> Self {
66        Assertion {
67            column,
68            first_step: step,
69            stride: NO_STRIDE,
70            values: vec![value],
71        }
72    }
73
74    /// Returns an single-value assertion against multiple cells of a single column.
75    ///
76    /// The returned assertion requires that values in the specified `column` must be equal to
77    /// the specified `value` at steps which start at `first_step` and repeat in equal intervals
78    /// specified by `stride`.
79    ///
80    /// # Panics
81    /// Panics if:
82    /// * `stride` is not a power of two, or is smaller than 2.
83    /// * `first_step` is greater than `stride`.
84    pub fn periodic(column: usize, first_step: usize, stride: usize, value: E) -> Self {
85        validate_stride(stride, first_step, column);
86        Assertion {
87            column,
88            first_step,
89            stride,
90            values: vec![value],
91        }
92    }
93
94    /// Returns a multi-value assertion against multiple cells of a single column.
95    ///
96    /// The returned assertion requires that values in the specified `column` must be equal to
97    /// the provided `values` at steps which start at `first_step` and repeat in equal intervals
98    /// specified by `stride` until all values have been consumed.
99    ///
100    /// # Panics
101    /// Panics if:
102    /// * `stride` is not a power of two, or is smaller than 2.
103    /// * `first_step` is greater than `stride`.
104    /// * `values` is empty or number of values in not a power of two.
105    pub fn sequence(column: usize, first_step: usize, stride: usize, values: Vec<E>) -> Self {
106        validate_stride(stride, first_step, column);
107        assert!(
108            !values.is_empty(),
109            "invalid assertion for column {column}: number of asserted values must be greater than zero"
110        );
111        assert!(
112            values.len().is_power_of_two(),
113            "invalid assertion for column {}: number of asserted values must be a power of two, but was {}",
114            column,
115            values.len()
116        );
117        Assertion {
118            column,
119            first_step,
120            stride: if values.len() == 1 { NO_STRIDE } else { stride },
121            values,
122        }
123    }
124
125    // PUBLIC ACCESSORS
126    // --------------------------------------------------------------------------------------------
127
128    /// Returns index of the column against which this assertion is placed.
129    pub fn column(&self) -> usize {
130        self.column
131    }
132
133    /// Returns the first step of the execution trace against which this assertion is placed.
134    ///
135    /// For single value assertions this is equivalent to the assertion step.
136    pub fn first_step(&self) -> usize {
137        self.first_step
138    }
139
140    /// Returns the interval at which the assertion repeats in the execution trace.
141    ///
142    /// For single value assertions, this will be 0.
143    pub fn stride(&self) -> usize {
144        self.stride
145    }
146
147    /// Returns asserted values.
148    ///
149    /// For single value and periodic assertions this will be a slice containing one value.
150    pub fn values(&self) -> &[E] {
151        &self.values
152    }
153
154    /// Returns true if this is a single-value assertion (one value, one step).
155    pub fn is_single(&self) -> bool {
156        self.stride == NO_STRIDE
157    }
158
159    /// Returns true if this is a periodic assertion (one value, many steps).
160    pub fn is_periodic(&self) -> bool {
161        self.stride != NO_STRIDE && self.values.len() == 1
162    }
163
164    /// Returns true if this is a sequence assertion (many values, many steps).
165    pub fn is_sequence(&self) -> bool {
166        self.values.len() > 1
167    }
168
169    // PUBLIC METHODS
170    // --------------------------------------------------------------------------------------------
171
172    /// Checks if this assertion overlaps with the provided assertion.
173    ///
174    /// Overlap is defined as asserting a value for the same step in the same column.
175    pub fn overlaps_with(&self, other: &Assertion<E>) -> bool {
176        if self.column != other.column {
177            return false;
178        }
179        if self.first_step == other.first_step {
180            return true;
181        }
182        if self.stride == other.stride {
183            return false;
184        }
185
186        // at this point we know that assertions are for the same column but they start
187        // on different steps and also have different strides
188
189        if self.first_step < other.first_step {
190            if self.is_single() {
191                return false;
192            }
193            if other.is_single() || self.stride < other.stride {
194                (other.first_step - self.first_step) % self.stride == 0
195            } else {
196                false
197            }
198        } else {
199            if other.is_single() {
200                return false;
201            }
202            if self.is_single() || other.stride < self.stride {
203                (self.first_step - other.first_step) % other.stride == 0
204            } else {
205                false
206            }
207        }
208    }
209
210    /// Panics if the assertion cannot be placed against an execution trace of the specified width.
211    pub fn validate_trace_width(&self, trace_width: usize) -> Result<(), AssertionError> {
212        if self.column >= trace_width {
213            return Err(AssertionError::TraceWidthTooShort(self.column, trace_width));
214        }
215        Ok(())
216    }
217
218    /// Checks if the assertion is valid against an execution trace of the specified length.
219    ///
220    /// # Errors
221    /// Returns an error if:
222    /// * `trace_length` is not a power of two.
223    /// * For single assertion, `first_step` >= `trace_length`.
224    /// * For periodic assertion, `stride` > `trace_length`.
225    /// * For sequence assertion, `num_values` * `stride` != `trace_length`;
226    pub fn validate_trace_length(&self, trace_length: usize) -> Result<(), AssertionError> {
227        if !trace_length.is_power_of_two() {
228            return Err(AssertionError::TraceLengthNotPowerOfTwo(trace_length));
229        }
230        if self.is_single() {
231            if self.first_step >= trace_length {
232                return Err(AssertionError::TraceLengthTooShort(
233                    (self.first_step + 1).next_power_of_two(),
234                    trace_length,
235                ));
236            }
237        } else if self.is_periodic() {
238            if self.stride > trace_length {
239                return Err(AssertionError::TraceLengthTooShort(self.stride, trace_length));
240            }
241        } else {
242            let expected_length = self.values.len() * self.stride;
243            if expected_length != trace_length {
244                return Err(AssertionError::TraceLengthNotExact(expected_length, trace_length));
245            }
246        }
247        Ok(())
248    }
249
250    /// Executes the provided closure for all possible instantiations of this assertions against
251    /// a execution trace of the specified length.
252    ///
253    /// # Panics
254    /// Panics if the specified trace length is not valid for this assertion.
255    pub fn apply<F>(&self, trace_length: usize, mut f: F)
256    where
257        F: FnMut(usize, E),
258    {
259        self.validate_trace_length(trace_length).unwrap_or_else(|err| {
260            panic!("invalid trace length: {err}");
261        });
262        if self.is_single() {
263            f(self.first_step, self.values[0]);
264        } else if self.is_periodic() {
265            for i in 0..(trace_length / self.stride) {
266                f(self.first_step + self.stride * i, self.values[0]);
267            }
268        } else {
269            for (i, &value) in self.values.iter().enumerate() {
270                f(self.first_step + self.stride * i, value);
271            }
272        }
273    }
274
275    /// Returns the number of steps against which this assertion will be applied given an
276    /// execution trace of the specified length.
277    ///
278    /// * For single-value assertions, this will always be one.
279    /// * For periodic assertions this will be equal to `trace_length` / `stride`.
280    /// * For sequence assertions this will be equal to the number of asserted values.
281    ///
282    /// # Panics
283    /// Panics if the specified trace length is not valid for this assertion.
284    pub fn get_num_steps(&self, trace_length: usize) -> usize {
285        self.validate_trace_length(trace_length).unwrap_or_else(|err| {
286            panic!("invalid trace length: {err}");
287        });
288        if self.is_single() {
289            1
290        } else if self.is_periodic() {
291            trace_length / self.stride
292        } else {
293            self.values.len()
294        }
295    }
296}
297
298// OTHER TRAIT IMPLEMENTATIONS
299// =================================================================================================
300
301/// We define ordering of assertions to be first by stride, then by first_step, and finally by
302/// column in ascending order.
303impl<E: FieldElement> Ord for Assertion<E> {
304    fn cmp(&self, other: &Self) -> Ordering {
305        if self.stride == other.stride {
306            if self.first_step == other.first_step {
307                self.column.partial_cmp(&other.column).unwrap()
308            } else {
309                self.first_step.partial_cmp(&other.first_step).unwrap()
310            }
311        } else {
312            self.stride.partial_cmp(&other.stride).unwrap()
313        }
314    }
315}
316
317impl<E: FieldElement> PartialOrd for Assertion<E> {
318    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
319        Some(self.cmp(other))
320    }
321}
322
323impl<E: FieldElement> Display for Assertion<E> {
324    fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
325        write!(f, "(column={}, ", self.column)?;
326        match self.stride {
327            0 => write!(f, "step={}, ", self.first_step)?,
328            _ => {
329                let second_step = self.first_step + self.stride;
330                write!(f, "steps=[{}, {}, ...], ", self.first_step, second_step)?;
331            },
332        }
333        match self.values.len() {
334            1 => write!(f, "value={})", self.values[0]),
335            2 => write!(f, "values=[{}, {}])", self.values[0], self.values[1]),
336            _ => write!(f, "values=[{}, {}, ...])", self.values[0], self.values[1]),
337        }
338    }
339}
340
341// HELPER FUNCTIONS
342// =================================================================================================
343
344fn validate_stride(stride: usize, first_step: usize, column: usize) {
345    assert!(
346        stride.is_power_of_two(),
347        "invalid assertion for column {column}: stride must be a power of two, but was {stride}"
348    );
349    assert!(
350        stride >= MIN_STRIDE_LENGTH,
351        "invalid assertion for column {column}: stride must be at least {MIN_STRIDE_LENGTH}, but was {stride}"
352    );
353    assert!(
354        first_step < stride,
355        "invalid assertion for column {column}: first step must be smaller than stride ({stride} steps), but was {first_step}"
356    );
357}