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}