1use std::ops::Mul;
2
3use arbitrary::Arbitrary;
4use arbitrary::Unstructured;
5use itertools::izip;
6use itertools::Itertools;
7use ndarray::prelude::*;
8use ndarray::Zip;
9use num_traits::ConstOne;
10use num_traits::ConstZero;
11use num_traits::Zero;
12use rand::prelude::*;
13use rand::random;
14use rayon::prelude::*;
15use serde::Deserialize;
16use serde::Serialize;
17use twenty_first::math::ntt::intt;
18use twenty_first::math::ntt::ntt;
19use twenty_first::math::traits::FiniteField;
20use twenty_first::math::traits::PrimitiveRootOfUnity;
21use twenty_first::prelude::*;
22
23use crate::aet::AlgebraicExecutionTrace;
24use crate::arithmetic_domain::ArithmeticDomain;
25use crate::challenges::Challenges;
26use crate::error::ProvingError;
27use crate::error::VerificationError;
28use crate::fri;
29use crate::fri::Fri;
30use crate::ndarray_helper;
31use crate::profiler::profiler;
32use crate::proof::Claim;
33use crate::proof::Proof;
34use crate::proof_item::ProofItem;
35use crate::proof_stream::ProofStream;
36use crate::table::auxiliary_table::Evaluable;
37use crate::table::master_table::all_quotients_combined;
38use crate::table::master_table::interpolant_degree;
39use crate::table::master_table::max_degree_with_origin;
40use crate::table::master_table::randomized_trace_len;
41use crate::table::master_table::MasterAuxTable;
42use crate::table::master_table::MasterMainTable;
43use crate::table::master_table::MasterTable;
44use crate::table::QuotientSegments;
45
46pub const NUM_QUOTIENT_SEGMENTS: usize = air::TARGET_DEGREE as usize;
49
50pub const NUM_RANDOMIZER_POLYNOMIALS: usize = 1;
53
54const NUM_DEEP_CODEWORD_COMPONENTS: usize = 3;
55
56#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
63pub struct Stark {
64 pub security_level: usize,
68
69 pub fri_expansion_factor: usize,
72
73 pub num_trace_randomizers: usize,
79
80 pub num_collinearity_checks: usize,
82}
83
84#[expect(missing_copy_implementations)]
94#[derive(Debug, Eq, PartialEq, Arbitrary)]
95pub struct Prover {
96 parameters: Stark,
97
98 randomness_seed: <StdRng as SeedableRng>::Seed,
104}
105
106#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash, Serialize, Deserialize, Arbitrary)]
110pub struct Verifier {
111 parameters: Stark,
112}
113
114impl Prover {
115 pub fn new(parameters: Stark) -> Self {
119 Self {
120 parameters,
121 randomness_seed: random(),
122 }
123 }
124
125 #[must_use]
143 pub fn set_randomness_seed_which_may_break_zero_knowledge(
144 mut self,
145 seed: <StdRng as SeedableRng>::Seed,
146 ) -> Self {
147 self.randomness_seed = seed;
148 self
149 }
150
151 pub fn prove(
153 self,
154 claim: &Claim,
155 aet: &AlgebraicExecutionTrace,
156 ) -> Result<Proof, ProvingError> {
157 profiler!(start "Fiat-Shamir: claim" ("hash"));
158 let mut proof_stream = ProofStream::new();
159 proof_stream.alter_fiat_shamir_state_with(claim);
160 profiler!(stop "Fiat-Shamir: claim");
161
162 profiler!(start "derive additional parameters");
163 let padded_height = aet.padded_height();
164 let max_degree = self.parameters.max_degree(padded_height);
165 let fri = self.parameters.fri(padded_height)?;
166 let quotient_domain = Self::quotient_domain(fri.domain, max_degree)?;
167 proof_stream.enqueue(ProofItem::Log2PaddedHeight(padded_height.ilog2()));
168 profiler!(stop "derive additional parameters");
169
170 profiler!(start "main tables");
171 profiler!(start "create" ("gen"));
172 let mut master_main_table = MasterMainTable::new(
173 aet,
174 quotient_domain,
175 fri.domain,
176 self.parameters.num_trace_randomizers,
177 self.randomness_seed,
178 );
179 profiler!(stop "create");
180
181 profiler!(start "pad" ("gen"));
182 master_main_table.pad();
183 profiler!(stop "pad");
184
185 master_main_table.maybe_low_degree_extend_all_columns();
186
187 profiler!(start "Merkle tree");
188 let main_merkle_tree = master_main_table.merkle_tree();
189 profiler!(stop "Merkle tree");
190
191 profiler!(start "Fiat-Shamir" ("hash"));
192 proof_stream.enqueue(ProofItem::MerkleRoot(main_merkle_tree.root()));
193 let challenges = proof_stream.sample_scalars(Challenges::SAMPLE_COUNT);
194 let challenges = Challenges::new(challenges, claim);
195 profiler!(stop "Fiat-Shamir");
196
197 profiler!(start "extend" ("gen"));
198 let mut master_aux_table = master_main_table.extend(&challenges);
199 profiler!(stop "extend");
200 profiler!(stop "main tables");
201
202 profiler!(start "aux tables");
203 master_aux_table.maybe_low_degree_extend_all_columns();
204
205 profiler!(start "Merkle tree");
206 let aux_merkle_tree = master_aux_table.merkle_tree();
207 profiler!(stop "Merkle tree");
208
209 profiler!(start "Fiat-Shamir" ("hash"));
210 proof_stream.enqueue(ProofItem::MerkleRoot(aux_merkle_tree.root()));
211
212 let quotient_combination_weights =
214 proof_stream.sample_scalars(MasterAuxTable::NUM_CONSTRAINTS);
215 profiler!(stop "Fiat-Shamir");
216 profiler!(stop "aux tables");
217
218 let (fri_domain_quotient_segment_codewords, quotient_segment_polynomials) =
219 Self::compute_quotient_segments(
220 &mut master_main_table,
221 &mut master_aux_table,
222 quotient_domain,
223 &challenges,
224 "ient_combination_weights,
225 );
226
227 profiler!(start "hash rows of quotient segments" ("hash"));
228 let interpret_xfe_as_bfes = |xfe: &XFieldElement| xfe.coefficients.to_vec();
229 let hash_row = |row: ArrayView1<_>| {
230 let row_as_bfes = row.iter().map(interpret_xfe_as_bfes).concat();
231 Tip5::hash_varlen(&row_as_bfes)
232 };
233 let quotient_segments_rows = fri_domain_quotient_segment_codewords
234 .axis_iter(Axis(0))
235 .into_par_iter();
236 let fri_domain_quotient_segment_codewords_digests =
237 quotient_segments_rows.map(hash_row).collect::<Vec<_>>();
238 profiler!(stop "hash rows of quotient segments");
239 profiler!(start "Merkle tree" ("hash"));
240 let quot_merkle_tree = MerkleTree::par_new(&fri_domain_quotient_segment_codewords_digests)?;
241 let quot_merkle_tree_root = quot_merkle_tree.root();
242 proof_stream.enqueue(ProofItem::MerkleRoot(quot_merkle_tree_root));
243 profiler!(stop "Merkle tree");
244
245 debug_assert_eq!(fri.domain.length, quot_merkle_tree.num_leafs());
246
247 profiler!(start "out-of-domain rows");
248 let trace_domain_generator = master_main_table.trace_domain().generator;
249 let out_of_domain_point_curr_row = proof_stream.sample_scalars(1)[0];
250 let out_of_domain_point_next_row = trace_domain_generator * out_of_domain_point_curr_row;
251
252 let ood_main_row = master_main_table.out_of_domain_row(out_of_domain_point_curr_row);
253 let ood_main_row = MasterMainTable::try_to_main_row(ood_main_row)?;
254 proof_stream.enqueue(ProofItem::OutOfDomainMainRow(Box::new(ood_main_row)));
255
256 let ood_aux_row = master_aux_table.out_of_domain_row(out_of_domain_point_curr_row);
257 let ood_aux_row = MasterAuxTable::try_to_aux_row(ood_aux_row)?;
258 proof_stream.enqueue(ProofItem::OutOfDomainAuxRow(Box::new(ood_aux_row)));
259
260 let ood_next_main_row = master_main_table.out_of_domain_row(out_of_domain_point_next_row);
261 let ood_next_main_row = MasterMainTable::try_to_main_row(ood_next_main_row)?;
262 proof_stream.enqueue(ProofItem::OutOfDomainMainRow(Box::new(ood_next_main_row)));
263
264 let ood_next_aux_row = master_aux_table.out_of_domain_row(out_of_domain_point_next_row);
265 let ood_next_aux_row = MasterAuxTable::try_to_aux_row(ood_next_aux_row)?;
266 proof_stream.enqueue(ProofItem::OutOfDomainAuxRow(Box::new(ood_next_aux_row)));
267
268 let out_of_domain_point_curr_row_pow_num_segments =
269 out_of_domain_point_curr_row.mod_pow_u32(NUM_QUOTIENT_SEGMENTS as u32);
270 let out_of_domain_curr_row_quot_segments = quotient_segment_polynomials
271 .map(|poly| poly.evaluate(out_of_domain_point_curr_row_pow_num_segments))
272 .to_vec()
273 .try_into()
274 .unwrap();
275 proof_stream.enqueue(ProofItem::OutOfDomainQuotientSegments(
276 out_of_domain_curr_row_quot_segments,
277 ));
278 profiler!(stop "out-of-domain rows");
279
280 profiler!(start "Fiat-Shamir" ("hash"));
281 let weights = LinearCombinationWeights::sample(&mut proof_stream);
282 profiler!(stop "Fiat-Shamir");
283
284 let fri_domain_is_short_domain = fri.domain.length <= quotient_domain.length;
285 let short_domain = if fri_domain_is_short_domain {
286 fri.domain
287 } else {
288 quotient_domain
289 };
290
291 profiler!(start "linear combination");
292 profiler!(start "main" ("CC"));
293 let main_combination_poly = master_main_table.weighted_sum_of_columns(weights.main);
294 profiler!(stop "main");
295
296 profiler!(start "aux" ("CC"));
297 let aux_combination_poly = master_aux_table.weighted_sum_of_columns(weights.aux);
298 profiler!(stop "aux");
299 let main_and_aux_combination_polynomial = main_combination_poly + aux_combination_poly;
300 let main_and_aux_codeword = short_domain.evaluate(&main_and_aux_combination_polynomial);
301
302 profiler!(start "quotient" ("CC"));
303 let quotient_segments_combination_polynomial = quotient_segment_polynomials
304 .into_iter()
305 .zip_eq(weights.quot_segments)
306 .fold(Polynomial::zero(), |acc, (poly, w)| acc + poly * w);
307 let quotient_segments_combination_codeword =
308 short_domain.evaluate("ient_segments_combination_polynomial);
309 profiler!(stop "quotient");
310
311 profiler!(stop "linear combination");
312
313 profiler!(start "DEEP");
314 profiler!(start "main&aux curr row");
331 let out_of_domain_curr_row_main_and_aux_value =
332 main_and_aux_combination_polynomial.evaluate(out_of_domain_point_curr_row);
333 let main_and_aux_curr_row_deep_codeword = Self::deep_codeword(
334 &main_and_aux_codeword,
335 short_domain,
336 out_of_domain_point_curr_row,
337 out_of_domain_curr_row_main_and_aux_value,
338 );
339 profiler!(stop "main&aux curr row");
340
341 profiler!(start "main&aux next row");
342 let out_of_domain_next_row_main_and_aux_value =
343 main_and_aux_combination_polynomial.evaluate(out_of_domain_point_next_row);
344 let main_and_aux_next_row_deep_codeword = Self::deep_codeword(
345 &main_and_aux_codeword,
346 short_domain,
347 out_of_domain_point_next_row,
348 out_of_domain_next_row_main_and_aux_value,
349 );
350 profiler!(stop "main&aux next row");
351
352 profiler!(start "segmented quotient");
353 let out_of_domain_curr_row_quot_segments_value = quotient_segments_combination_polynomial
354 .evaluate(out_of_domain_point_curr_row_pow_num_segments);
355 let quotient_segments_curr_row_deep_codeword = Self::deep_codeword(
356 "ient_segments_combination_codeword,
357 short_domain,
358 out_of_domain_point_curr_row_pow_num_segments,
359 out_of_domain_curr_row_quot_segments_value,
360 );
361 profiler!(stop "segmented quotient");
362 profiler!(stop "DEEP");
363
364 profiler!(start "combined DEEP polynomial");
365 profiler!(start "sum" ("CC"));
366 let deep_codeword = [
367 main_and_aux_curr_row_deep_codeword,
368 main_and_aux_next_row_deep_codeword,
369 quotient_segments_curr_row_deep_codeword,
370 ]
371 .into_par_iter()
372 .zip_eq(weights.deep.as_slice().unwrap())
373 .map(|(codeword, &weight)| codeword.into_par_iter().map(|c| c * weight).collect())
374 .reduce(
375 || vec![XFieldElement::ZERO; short_domain.length],
376 |left, right| left.into_iter().zip(right).map(|(l, r)| l + r).collect(),
377 );
378
379 profiler!(stop "sum");
380 let fri_combination_codeword = if fri_domain_is_short_domain {
381 deep_codeword
382 } else {
383 profiler!(start "LDE" ("LDE"));
384 let deep_codeword = quotient_domain.low_degree_extension(&deep_codeword, fri.domain);
385 profiler!(stop "LDE");
386 deep_codeword
387 };
388 assert_eq!(fri.domain.length, fri_combination_codeword.len());
389 profiler!(stop "combined DEEP polynomial");
390
391 profiler!(start "FRI");
392 let revealed_current_row_indices =
393 fri.prove(&fri_combination_codeword, &mut proof_stream)?;
394 assert_eq!(
395 self.parameters.num_collinearity_checks,
396 revealed_current_row_indices.len()
397 );
398 profiler!(stop "FRI");
399
400 profiler!(start "open trace leafs");
401 let main_row_err = |row: Vec<_>| ProvingError::TableRowConversionError {
403 expected_len: MasterMainTable::NUM_COLUMNS,
404 actual_len: row.len(),
405 };
406 let revealed_main_elems = master_main_table
407 .reveal_rows(&revealed_current_row_indices)
408 .into_iter()
409 .map(|row| row.try_into().map_err(main_row_err))
410 .try_collect()?;
411 let base_authentication_structure =
412 main_merkle_tree.authentication_structure(&revealed_current_row_indices)?;
413 proof_stream.enqueue(ProofItem::MasterMainTableRows(revealed_main_elems));
414 proof_stream.enqueue(ProofItem::AuthenticationStructure(
415 base_authentication_structure,
416 ));
417
418 let aux_row_err = |row: Vec<_>| ProvingError::TableRowConversionError {
419 expected_len: MasterAuxTable::NUM_COLUMNS,
420 actual_len: row.len(),
421 };
422 let revealed_aux_elems = master_aux_table
423 .reveal_rows(&revealed_current_row_indices)
424 .into_iter()
425 .map(|row| row.try_into().map_err(aux_row_err))
426 .try_collect()?;
427 let aux_authentication_structure =
428 aux_merkle_tree.authentication_structure(&revealed_current_row_indices)?;
429 proof_stream.enqueue(ProofItem::MasterAuxTableRows(revealed_aux_elems));
430 proof_stream.enqueue(ProofItem::AuthenticationStructure(
431 aux_authentication_structure,
432 ));
433
434 let into_fixed_width_row =
436 |row: ArrayView1<_>| -> QuotientSegments { row.to_vec().try_into().unwrap() };
437 let revealed_quotient_segments_rows = revealed_current_row_indices
438 .iter()
439 .map(|&i| fri_domain_quotient_segment_codewords.row(i))
440 .map(into_fixed_width_row)
441 .collect_vec();
442 let revealed_quotient_authentication_structure =
443 quot_merkle_tree.authentication_structure(&revealed_current_row_indices)?;
444 proof_stream.enqueue(ProofItem::QuotientSegmentsElements(
445 revealed_quotient_segments_rows,
446 ));
447 proof_stream.enqueue(ProofItem::AuthenticationStructure(
448 revealed_quotient_authentication_structure,
449 ));
450 profiler!(stop "open trace leafs");
451
452 Ok(proof_stream.into())
453 }
454
455 pub(crate) fn quotient_domain(
461 fri_domain: ArithmeticDomain,
462 max_degree: isize,
463 ) -> Result<ArithmeticDomain, ProvingError> {
464 let max_degree = usize::try_from(max_degree).expect("AIR should constrain the VM");
465 let domain_length = max_degree.next_power_of_two();
466 Ok(ArithmeticDomain::of_length(domain_length)?.with_offset(fri_domain.offset))
467 }
468
469 fn compute_quotient_segments(
470 main_table: &mut MasterMainTable,
471 aux_table: &mut MasterAuxTable,
472 quotient_domain: ArithmeticDomain,
473 challenges: &Challenges,
474 quotient_combination_weights: &[XFieldElement],
475 ) -> (
476 Array2<XFieldElement>,
477 Array1<Polynomial<'static, XFieldElement>>,
478 ) {
479 let (Some(main_quotient_domain_codewords), Some(aux_quotient_domain_codewords)) = (
480 main_table.quotient_domain_table(),
481 aux_table.quotient_domain_table(),
482 ) else {
483 main_table.clear_cache();
499 aux_table.clear_cache();
500
501 profiler!(start "quotient calculation (just-in-time)");
502 let (fri_domain_quotient_segment_codewords, quotient_segment_polynomials) =
503 Self::compute_quotient_segments_with_jit_lde(
504 main_table,
505 aux_table,
506 challenges,
507 quotient_combination_weights,
508 );
509 profiler!(stop "quotient calculation (just-in-time)");
510
511 return (
512 fri_domain_quotient_segment_codewords,
513 quotient_segment_polynomials,
514 );
515 };
516
517 profiler!(start "quotient calculation (cached)" ("CC"));
518 let quotient_codeword = all_quotients_combined(
519 main_quotient_domain_codewords,
520 aux_quotient_domain_codewords,
521 main_table.trace_domain(),
522 quotient_domain,
523 challenges,
524 quotient_combination_weights,
525 );
526 let quotient_codeword = Array1::from(quotient_codeword);
527 assert_eq!(quotient_domain.length, quotient_codeword.len());
528 profiler!(stop "quotient calculation (cached)");
529
530 profiler!(start "quotient LDE" ("LDE"));
531 let quotient_segment_polynomials =
532 Self::interpolate_quotient_segments(quotient_codeword, quotient_domain);
533
534 let fri_domain = main_table.fri_domain();
535 let fri_domain_quotient_segment_codewords =
536 Self::fri_domain_segment_polynomials(quotient_segment_polynomials.view(), fri_domain);
537 profiler!(stop "quotient LDE");
538
539 (
540 fri_domain_quotient_segment_codewords,
541 quotient_segment_polynomials,
542 )
543 }
544
545 fn compute_quotient_segments_with_jit_lde(
551 main_table: &mut MasterMainTable,
552 aux_table: &mut MasterAuxTable,
553 challenges: &Challenges,
554 quotient_combination_weights: &[XFieldElement],
555 ) -> (
556 Array2<XFieldElement>,
557 Array1<Polynomial<'static, XFieldElement>>,
558 ) {
559 const RANDOMIZED_TRACE_LEN_TO_WORKING_DOMAIN_LEN_RATIO: usize = 2;
570 const NUM_COSETS: usize =
571 NUM_QUOTIENT_SEGMENTS * RANDOMIZED_TRACE_LEN_TO_WORKING_DOMAIN_LEN_RATIO;
572
573 debug_assert!(RANDOMIZED_TRACE_LEN_TO_WORKING_DOMAIN_LEN_RATIO.is_power_of_two());
574
575 let mut working_domain = main_table.randomized_trace_domain();
576 for _ in 0..RANDOMIZED_TRACE_LEN_TO_WORKING_DOMAIN_LEN_RATIO.ilog2() {
577 working_domain = working_domain.halve().unwrap();
578 }
579 let working_domain = working_domain;
580 let trace_domain = main_table.trace_domain();
581 let fri_domain = main_table.fri_domain();
582
583 let num_rows = working_domain.length;
584 let coset_root_order = (num_rows * NUM_COSETS).try_into().unwrap();
585
586 let iota = BFieldElement::primitive_root_of_unity(coset_root_order).unwrap();
588 let psi = fri_domain.offset;
589
590 profiler!(start "zero-initialization");
592 let mut quotient_multicoset_evaluations =
594 ndarray_helper::par_zeros((num_rows, NUM_COSETS).f());
595 let mut main_columns =
596 ndarray_helper::par_zeros((num_rows, MasterMainTable::NUM_COLUMNS).f());
597 let mut aux_columns =
598 ndarray_helper::par_zeros((num_rows, MasterAuxTable::NUM_COLUMNS).f());
599 profiler!(stop "zero-initialization");
600
601 profiler!(start "fetch trace randomizers");
602 let main_trace_randomizers = (0..MasterMainTable::NUM_COLUMNS)
603 .into_par_iter()
604 .map(|i| main_table.trace_randomizer_for_column(i))
605 .collect::<Vec<_>>();
606 let aux_trace_randomizers = (0..MasterAuxTable::NUM_COLUMNS)
607 .into_par_iter()
608 .map(|i| aux_table.trace_randomizer_for_column(i))
609 .collect::<Vec<_>>();
610 let main_trace_randomizers = Array1::from(main_trace_randomizers);
611 let aux_trace_randomizers = Array1::from(aux_trace_randomizers);
612 profiler!(stop "fetch trace randomizers");
613
614 profiler!(start "poly interpolate" ("LDE"));
615 main_table
616 .trace_table_mut()
617 .axis_iter_mut(Axis(1))
618 .into_par_iter()
619 .for_each(|mut column| intt(column.as_slice_mut().unwrap()));
620 aux_table
621 .trace_table_mut()
622 .axis_iter_mut(Axis(1))
623 .into_par_iter()
624 .for_each(|mut column| intt(column.as_slice_mut().unwrap()));
625 profiler!(stop "poly interpolate");
626
627 profiler!(start "calculate quotients");
628 for (coset_index, quotient_column) in
629 (0..).zip(quotient_multicoset_evaluations.columns_mut())
630 {
631 let working_domain = working_domain.with_offset(iota.mod_pow(coset_index) * psi);
633 profiler!(start "poly evaluate" ("LDE"));
634 Zip::from(main_table.trace_table().axis_iter(Axis(1)))
635 .and(main_columns.axis_iter_mut(Axis(1)))
636 .par_for_each(|trace_column, target_column| {
637 let trace_poly = Polynomial::new_borrowed(trace_column.as_slice().unwrap());
638 Array1::from(working_domain.evaluate(&trace_poly)).move_into(target_column);
639 });
640 Zip::from(aux_table.trace_table().axis_iter(Axis(1)))
641 .and(aux_columns.axis_iter_mut(Axis(1)))
642 .par_for_each(|trace_column, target_column| {
643 let trace_poly = Polynomial::new_borrowed(trace_column.as_slice().unwrap());
644 Array1::from(working_domain.evaluate(&trace_poly)).move_into(target_column);
645 });
646 profiler!(stop "poly evaluate");
647
648 assert!(working_domain.length <= trace_domain.length);
689
690 profiler!(start "trace randomizers" ("LDE"));
691 let trace_domain_len = u64::try_from(trace_domain.length).unwrap();
692 let zerofier = working_domain.offset.mod_pow(trace_domain_len) - BFieldElement::ONE;
693
694 Zip::from(main_columns.axis_iter_mut(Axis(1)))
695 .and(main_trace_randomizers.axis_iter(Axis(0)))
696 .par_for_each(|mut column, randomizer_polynomial| {
697 let randomizer_codeword = working_domain.evaluate(&randomizer_polynomial[[]]);
698 for (cell, randomizer) in column.iter_mut().zip(randomizer_codeword) {
699 *cell += zerofier * randomizer;
700 }
701 });
702 Zip::from(aux_columns.axis_iter_mut(Axis(1)))
703 .and(aux_trace_randomizers.axis_iter(Axis(0)))
704 .par_for_each(|mut column, randomizer_polynomial| {
705 let randomizer_codeword = working_domain.evaluate(&randomizer_polynomial[[]]);
706 for (cell, randomizer) in column.iter_mut().zip(randomizer_codeword) {
707 *cell += zerofier * randomizer;
708 }
709 });
710 profiler!(stop "trace randomizers");
711
712 profiler!(start "AIR evaluation" ("AIR"));
713 let all_quotients = all_quotients_combined(
714 main_columns.view(),
715 aux_columns.view(),
716 trace_domain,
717 working_domain,
718 challenges,
719 quotient_combination_weights,
720 );
721 Array1::from(all_quotients).move_into(quotient_column);
722 profiler!(stop "AIR evaluation");
723 }
724 profiler!(stop "calculate quotients");
725
726 profiler!(start "segmentify");
727 let segmentification = Self::segmentify::<NUM_QUOTIENT_SEGMENTS>(
728 quotient_multicoset_evaluations,
729 psi,
730 iota,
731 fri_domain,
732 );
733 profiler!(stop "segmentify");
734
735 profiler!(start "restore original trace" ("LDE"));
736 main_table
737 .trace_table_mut()
738 .axis_iter_mut(Axis(1))
739 .into_par_iter()
740 .for_each(|mut column| ntt(column.as_slice_mut().unwrap()));
741 aux_table
742 .trace_table_mut()
743 .axis_iter_mut(Axis(1))
744 .into_par_iter()
745 .for_each(|mut column| ntt(column.as_slice_mut().unwrap()));
746 profiler!(stop "restore original trace");
747
748 segmentification
749 }
750
751 fn segmentify<const NUM_SEGMENTS: usize>(
870 quotient_multicoset_evaluations: Array2<XFieldElement>,
871 psi: BFieldElement,
872 iota: BFieldElement,
873 fri_domain: ArithmeticDomain,
874 ) -> (
875 Array2<XFieldElement>,
876 Array1<Polynomial<'static, XFieldElement>>,
877 ) {
878 let num_input_rows = quotient_multicoset_evaluations.nrows();
879 let num_cosets = quotient_multicoset_evaluations.ncols();
880 let num_output_rows = num_input_rows * num_cosets / NUM_SEGMENTS;
881
882 assert!(num_input_rows.is_power_of_two());
883 assert!(num_cosets.is_power_of_two());
884 assert!(num_output_rows.is_power_of_two());
885 assert!(NUM_SEGMENTS.is_power_of_two());
886 assert!(
887 num_input_rows >= num_cosets,
888 "working domain length: {num_input_rows} versus num cosets: {num_cosets}",
889 );
890 assert!(
891 num_cosets >= NUM_SEGMENTS,
892 "num cosets: {num_cosets} versus num segments: {NUM_SEGMENTS}",
893 );
894
895 let mut quotient_segments = ndarray_helper::par_zeros((num_output_rows, NUM_SEGMENTS));
900 quotient_segments
901 .axis_iter_mut(Axis(0))
902 .into_par_iter()
903 .enumerate()
904 .for_each(|(output_row_idx, mut output_row)| {
905 for (output_col_idx, cell) in output_row.iter_mut().enumerate() {
906 let exponent_of_iota = output_row_idx + output_col_idx * num_output_rows;
907 let input_row_idx = exponent_of_iota / num_cosets;
908 let input_col_idx = exponent_of_iota % num_cosets;
909 *cell = quotient_multicoset_evaluations[[input_row_idx, input_col_idx]];
910 }
911 });
912
913 quotient_segments
915 .axis_iter_mut(Axis(0))
916 .into_par_iter()
917 .for_each(|mut row| intt(row.as_slice_mut().unwrap()));
918
919 let num_threads = std::thread::available_parallelism()
921 .map(|t| t.get())
922 .unwrap_or(1);
923 let chunk_size = (num_output_rows / num_threads).max(1);
924 let iota_inverse = iota.inverse();
925 let psi_inverse = psi.inverse();
926 quotient_segments
927 .axis_chunks_iter_mut(Axis(0), chunk_size)
928 .into_par_iter()
929 .enumerate()
930 .for_each(|(chunk_idx, mut chunk_of_rows)| {
931 let chunk_start = (chunk_idx * chunk_size).try_into().unwrap();
932 let mut psi_iotajim_inv = psi_inverse * iota_inverse.mod_pow(chunk_start);
933 for mut row in chunk_of_rows.rows_mut() {
934 let mut psi_iotajim_invk = XFieldElement::ONE;
935 for cell in &mut row {
936 *cell *= psi_iotajim_invk;
937 psi_iotajim_invk *= psi_iotajim_inv;
938 }
939 psi_iotajim_inv *= iota_inverse;
940 }
941 });
942
943 let segment_domain_offset = psi.mod_pow(NUM_SEGMENTS.try_into().unwrap());
945 let segment_domain = ArithmeticDomain::of_length(num_output_rows)
946 .unwrap()
947 .with_offset(segment_domain_offset);
948
949 let mut quotient_codewords = Array2::zeros([fri_domain.length, NUM_SEGMENTS]);
950 let mut quotient_polynomials = Array1::zeros([NUM_SEGMENTS]);
951 Zip::from(quotient_segments.axis_iter(Axis(1)))
952 .and(quotient_codewords.axis_iter_mut(Axis(1)))
953 .and(quotient_polynomials.axis_iter_mut(Axis(0)))
954 .par_for_each(|segment, target_codeword, target_polynomial| {
955 let interpolant = segment_domain.interpolate(&segment.to_vec());
957 let lde_codeword = fri_domain.evaluate(&interpolant);
958 Array1::from(lde_codeword).move_into(target_codeword);
959 Array0::from_elem((), interpolant).move_into(target_polynomial);
960 });
961
962 (quotient_codewords, quotient_polynomials)
963 }
964
965 fn interpolate_quotient_segments(
966 quotient_codeword: Array1<XFieldElement>,
967 quotient_domain: ArithmeticDomain,
968 ) -> Array1<Polynomial<'static, XFieldElement>> {
969 let quotient_interpolation_poly = quotient_domain.interpolate("ient_codeword.to_vec());
970 let quotient_segments: [_; NUM_QUOTIENT_SEGMENTS] =
971 Self::split_polynomial_into_segments(quotient_interpolation_poly);
972 Array1::from(quotient_segments.to_vec())
973 }
974
975 fn split_polynomial_into_segments<const N: usize, FF: FiniteField>(
993 polynomial: Polynomial<FF>,
994 ) -> [Polynomial<'static, FF>; N] {
995 let mut segments = Vec::with_capacity(N);
996 let coefficients = polynomial.into_coefficients();
997 for segment_index in 0..N {
998 let segment_coefficients = coefficients.iter().skip(segment_index).step_by(N);
999 let segment = Polynomial::new(segment_coefficients.copied().collect());
1000 segments.push(segment);
1001 }
1002 segments.try_into().unwrap()
1003 }
1004
1005 fn fri_domain_segment_polynomials(
1006 quotient_segment_polynomials: ArrayView1<Polynomial<XFieldElement>>,
1007 fri_domain: ArithmeticDomain,
1008 ) -> Array2<XFieldElement> {
1009 let fri_domain_codewords: Vec<_> = quotient_segment_polynomials
1010 .into_par_iter()
1011 .flat_map(|segment| fri_domain.evaluate(segment))
1012 .collect();
1013 Array2::from_shape_vec(
1014 [fri_domain.length, NUM_QUOTIENT_SEGMENTS].f(),
1015 fri_domain_codewords,
1016 )
1017 .unwrap()
1018 }
1019
1020 fn deep_codeword(
1023 codeword: &[XFieldElement],
1024 domain: ArithmeticDomain,
1025 out_of_domain_point: XFieldElement,
1026 out_of_domain_value: XFieldElement,
1027 ) -> Vec<XFieldElement> {
1028 domain
1029 .domain_values()
1030 .par_iter()
1031 .zip_eq(codeword)
1032 .map(|(&in_domain_value, &in_domain_evaluation)| {
1033 Stark::deep_update(
1034 in_domain_value,
1035 in_domain_evaluation,
1036 out_of_domain_point,
1037 out_of_domain_value,
1038 )
1039 })
1040 .collect()
1041 }
1042}
1043
1044impl Verifier {
1045 pub fn new(parameters: Stark) -> Self {
1046 Self { parameters }
1047 }
1048
1049 pub fn verify(self, claim: &Claim, proof: &Proof) -> Result<(), VerificationError> {
1051 profiler!(start "deserialize");
1052 let mut proof_stream = ProofStream::try_from(proof)?;
1053 profiler!(stop "deserialize");
1054
1055 profiler!(start "Fiat-Shamir: Claim" ("hash"));
1056 proof_stream.alter_fiat_shamir_state_with(claim);
1057 profiler!(stop "Fiat-Shamir: Claim");
1058
1059 profiler!(start "derive additional parameters");
1060 let log_2_padded_height = proof_stream.dequeue()?.try_into_log2_padded_height()?;
1061 let padded_height = 1 << log_2_padded_height;
1062 let fri = self.parameters.fri(padded_height)?;
1063 let merkle_tree_height = fri.domain.length.ilog2() as usize;
1064 profiler!(stop "derive additional parameters");
1065
1066 profiler!(start "Fiat-Shamir 1" ("hash"));
1067 let main_merkle_tree_root = proof_stream.dequeue()?.try_into_merkle_root()?;
1068 let extension_challenge_weights = proof_stream.sample_scalars(Challenges::SAMPLE_COUNT);
1069 let challenges = Challenges::new(extension_challenge_weights, claim);
1070 let auxiliary_tree_merkle_root = proof_stream.dequeue()?.try_into_merkle_root()?;
1071 let quot_codeword_weights = proof_stream.sample_scalars(MasterAuxTable::NUM_CONSTRAINTS);
1074 let quot_codeword_weights = Array1::from(quot_codeword_weights);
1075 let quotient_codeword_merkle_root = proof_stream.dequeue()?.try_into_merkle_root()?;
1076 profiler!(stop "Fiat-Shamir 1");
1077
1078 profiler!(start "dequeue ood point and rows" ("hash"));
1079 let trace_domain_generator = ArithmeticDomain::generator_for_length(padded_height as u64)?;
1080 let out_of_domain_point_curr_row = proof_stream.sample_scalars(1)[0];
1081 let out_of_domain_point_next_row = trace_domain_generator * out_of_domain_point_curr_row;
1082 let out_of_domain_point_curr_row_pow_num_segments =
1083 out_of_domain_point_curr_row.mod_pow_u32(NUM_QUOTIENT_SEGMENTS as u32);
1084
1085 let out_of_domain_curr_main_row =
1086 proof_stream.dequeue()?.try_into_out_of_domain_main_row()?;
1087 let out_of_domain_curr_aux_row =
1088 proof_stream.dequeue()?.try_into_out_of_domain_aux_row()?;
1089 let out_of_domain_next_main_row =
1090 proof_stream.dequeue()?.try_into_out_of_domain_main_row()?;
1091 let out_of_domain_next_aux_row =
1092 proof_stream.dequeue()?.try_into_out_of_domain_aux_row()?;
1093 let out_of_domain_curr_row_quot_segments = proof_stream
1094 .dequeue()?
1095 .try_into_out_of_domain_quot_segments()?;
1096
1097 let out_of_domain_curr_main_row = Array1::from(out_of_domain_curr_main_row.to_vec());
1098 let out_of_domain_curr_aux_row = Array1::from(out_of_domain_curr_aux_row.to_vec());
1099 let out_of_domain_next_main_row = Array1::from(out_of_domain_next_main_row.to_vec());
1100 let out_of_domain_next_aux_row = Array1::from(out_of_domain_next_aux_row.to_vec());
1101 let out_of_domain_curr_row_quot_segments =
1102 Array1::from(out_of_domain_curr_row_quot_segments.to_vec());
1103 profiler!(stop "dequeue ood point and rows");
1104
1105 profiler!(start "out-of-domain quotient element");
1106 profiler!(start "evaluate AIR" ("AIR"));
1107 let evaluated_initial_constraints = MasterAuxTable::evaluate_initial_constraints(
1108 out_of_domain_curr_main_row.view(),
1109 out_of_domain_curr_aux_row.view(),
1110 &challenges,
1111 );
1112 let evaluated_consistency_constraints = MasterAuxTable::evaluate_consistency_constraints(
1113 out_of_domain_curr_main_row.view(),
1114 out_of_domain_curr_aux_row.view(),
1115 &challenges,
1116 );
1117 let evaluated_transition_constraints = MasterAuxTable::evaluate_transition_constraints(
1118 out_of_domain_curr_main_row.view(),
1119 out_of_domain_curr_aux_row.view(),
1120 out_of_domain_next_main_row.view(),
1121 out_of_domain_next_aux_row.view(),
1122 &challenges,
1123 );
1124 let evaluated_terminal_constraints = MasterAuxTable::evaluate_terminal_constraints(
1125 out_of_domain_curr_main_row.view(),
1126 out_of_domain_curr_aux_row.view(),
1127 &challenges,
1128 );
1129 profiler!(stop "evaluate AIR");
1130
1131 profiler!(start "zerofiers");
1132 let initial_zerofier_inv = (out_of_domain_point_curr_row - bfe!(1)).inverse();
1133 let consistency_zerofier_inv =
1134 (out_of_domain_point_curr_row.mod_pow_u32(padded_height as u32) - bfe!(1)).inverse();
1135 let except_last_row = out_of_domain_point_curr_row - trace_domain_generator.inverse();
1136 let transition_zerofier_inv = except_last_row * consistency_zerofier_inv;
1137 let terminal_zerofier_inv = except_last_row.inverse(); profiler!(stop "zerofiers");
1139
1140 profiler!(start "divide");
1141 let divide = |constraints: Vec<_>, z_inv| constraints.into_iter().map(move |c| c * z_inv);
1142 let initial_quotients = divide(evaluated_initial_constraints, initial_zerofier_inv);
1143 let consistency_quotients =
1144 divide(evaluated_consistency_constraints, consistency_zerofier_inv);
1145 let transition_quotients =
1146 divide(evaluated_transition_constraints, transition_zerofier_inv);
1147 let terminal_quotients = divide(evaluated_terminal_constraints, terminal_zerofier_inv);
1148
1149 let quotient_summands = initial_quotients
1150 .chain(consistency_quotients)
1151 .chain(transition_quotients)
1152 .chain(terminal_quotients)
1153 .collect_vec();
1154 profiler!(stop "divide");
1155
1156 profiler!(start "inner product" ("CC"));
1157 let out_of_domain_quotient_value =
1158 quot_codeword_weights.dot(&Array1::from(quotient_summands));
1159 profiler!(stop "inner product");
1160 profiler!(stop "out-of-domain quotient element");
1161
1162 profiler!(start "verify quotient's segments");
1163 let powers_of_out_of_domain_point_curr_row = (0..NUM_QUOTIENT_SEGMENTS as u32)
1164 .map(|exponent| out_of_domain_point_curr_row.mod_pow_u32(exponent))
1165 .collect::<Array1<_>>();
1166 let sum_of_evaluated_out_of_domain_quotient_segments =
1167 powers_of_out_of_domain_point_curr_row.dot(&out_of_domain_curr_row_quot_segments);
1168 if out_of_domain_quotient_value != sum_of_evaluated_out_of_domain_quotient_segments {
1169 return Err(VerificationError::OutOfDomainQuotientValueMismatch);
1170 };
1171 profiler!(stop "verify quotient's segments");
1172
1173 profiler!(start "Fiat-Shamir 2" ("hash"));
1174 let weights = LinearCombinationWeights::sample(&mut proof_stream);
1175 let main_and_aux_codeword_weights = weights.main_and_aux();
1176 profiler!(stop "Fiat-Shamir 2");
1177
1178 profiler!(start "sum out-of-domain values" ("CC"));
1179 let out_of_domain_curr_row_main_and_aux_value = Self::linearly_sum_main_and_aux_row(
1180 out_of_domain_curr_main_row.view(),
1181 out_of_domain_curr_aux_row.view(),
1182 main_and_aux_codeword_weights.view(),
1183 );
1184 let out_of_domain_next_row_main_and_aux_value = Self::linearly_sum_main_and_aux_row(
1185 out_of_domain_next_main_row.view(),
1186 out_of_domain_next_aux_row.view(),
1187 main_and_aux_codeword_weights.view(),
1188 );
1189 let out_of_domain_curr_row_quotient_segment_value = weights
1190 .quot_segments
1191 .dot(&out_of_domain_curr_row_quot_segments);
1192 profiler!(stop "sum out-of-domain values");
1193
1194 profiler!(start "FRI");
1196 let revealed_fri_indices_and_elements = fri.verify(&mut proof_stream)?;
1197 let (revealed_current_row_indices, revealed_fri_values): (Vec<_>, Vec<_>) =
1198 revealed_fri_indices_and_elements.into_iter().unzip();
1199 profiler!(stop "FRI");
1200
1201 profiler!(start "check leafs");
1202 profiler!(start "dequeue main elements");
1203 let main_table_rows = proof_stream.dequeue()?.try_into_master_main_table_rows()?;
1204 let main_authentication_structure = proof_stream
1205 .dequeue()?
1206 .try_into_authentication_structure()?;
1207 let leaf_digests_main: Vec<_> = main_table_rows
1208 .par_iter()
1209 .map(|revealed_main_elem| Tip5::hash_varlen(revealed_main_elem))
1210 .collect();
1211 profiler!(stop "dequeue main elements");
1212
1213 let index_leaves = |leaves| {
1214 let index_iter = revealed_current_row_indices.iter().copied();
1215 index_iter.zip_eq(leaves).collect()
1216 };
1217 profiler!(start "Merkle verify (main tree)" ("hash"));
1218 let base_merkle_tree_inclusion_proof = MerkleTreeInclusionProof {
1219 tree_height: merkle_tree_height,
1220 indexed_leafs: index_leaves(leaf_digests_main),
1221 authentication_structure: main_authentication_structure,
1222 };
1223 if !base_merkle_tree_inclusion_proof.verify(main_merkle_tree_root) {
1224 return Err(VerificationError::MainCodewordAuthenticationFailure);
1225 }
1226 profiler!(stop "Merkle verify (main tree)");
1227
1228 profiler!(start "dequeue auxiliary elements");
1229 let aux_table_rows = proof_stream.dequeue()?.try_into_master_aux_table_rows()?;
1230 let aux_authentication_structure = proof_stream
1231 .dequeue()?
1232 .try_into_authentication_structure()?;
1233 let leaf_digests_aux = aux_table_rows
1234 .par_iter()
1235 .map(|xvalues| {
1236 let b_values = xvalues.iter().flat_map(|xfe| xfe.coefficients.to_vec());
1237 Tip5::hash_varlen(&b_values.collect_vec())
1238 })
1239 .collect::<Vec<_>>();
1240 profiler!(stop "dequeue auxiliary elements");
1241
1242 profiler!(start "Merkle verify (auxiliary tree)" ("hash"));
1243 let aux_merkle_tree_inclusion_proof = MerkleTreeInclusionProof {
1244 tree_height: merkle_tree_height,
1245 indexed_leafs: index_leaves(leaf_digests_aux),
1246 authentication_structure: aux_authentication_structure,
1247 };
1248 if !aux_merkle_tree_inclusion_proof.verify(auxiliary_tree_merkle_root) {
1249 return Err(VerificationError::AuxiliaryCodewordAuthenticationFailure);
1250 }
1251 profiler!(stop "Merkle verify (auxiliary tree)");
1252
1253 profiler!(start "dequeue quotient segments' elements");
1254 let revealed_quotient_segments_elements =
1255 proof_stream.dequeue()?.try_into_quot_segments_elements()?;
1256 let revealed_quotient_segments_digests =
1257 Self::hash_quotient_segment_elements(&revealed_quotient_segments_elements);
1258 let revealed_quotient_authentication_structure = proof_stream
1259 .dequeue()?
1260 .try_into_authentication_structure()?;
1261 profiler!(stop "dequeue quotient segments' elements");
1262
1263 profiler!(start "Merkle verify (combined quotient)" ("hash"));
1264 let quot_merkle_tree_inclusion_proof = MerkleTreeInclusionProof {
1265 tree_height: merkle_tree_height,
1266 indexed_leafs: index_leaves(revealed_quotient_segments_digests),
1267 authentication_structure: revealed_quotient_authentication_structure,
1268 };
1269 if !quot_merkle_tree_inclusion_proof.verify(quotient_codeword_merkle_root) {
1270 return Err(VerificationError::QuotientCodewordAuthenticationFailure);
1271 }
1272 profiler!(stop "Merkle verify (combined quotient)");
1273 profiler!(stop "check leafs");
1274
1275 profiler!(start "linear combination");
1276 if self.parameters.num_collinearity_checks != revealed_current_row_indices.len() {
1277 return Err(VerificationError::IncorrectNumberOfRowIndices);
1278 };
1279 if self.parameters.num_collinearity_checks != revealed_fri_values.len() {
1280 return Err(VerificationError::IncorrectNumberOfFRIValues);
1281 };
1282 if self.parameters.num_collinearity_checks != revealed_quotient_segments_elements.len() {
1283 return Err(VerificationError::IncorrectNumberOfQuotientSegmentElements);
1284 };
1285 if self.parameters.num_collinearity_checks != main_table_rows.len() {
1286 return Err(VerificationError::IncorrectNumberOfMainTableRows);
1287 };
1288 if self.parameters.num_collinearity_checks != aux_table_rows.len() {
1289 return Err(VerificationError::IncorrectNumberOfAuxTableRows);
1290 };
1291
1292 for (row_idx, main_row, aux_row, quotient_segments_elements, fri_value) in izip!(
1293 revealed_current_row_indices,
1294 main_table_rows,
1295 aux_table_rows,
1296 revealed_quotient_segments_elements,
1297 revealed_fri_values,
1298 ) {
1299 let main_row = Array1::from(main_row.to_vec());
1300 let aux_row = Array1::from(aux_row.to_vec());
1301 let current_fri_domain_value = fri.domain.domain_value(row_idx as u32);
1302
1303 profiler!(start "main & aux elements" ("CC"));
1304 let main_and_aux_curr_row_element = Self::linearly_sum_main_and_aux_row(
1305 main_row.view(),
1306 aux_row.view(),
1307 main_and_aux_codeword_weights.view(),
1308 );
1309 let quotient_segments_curr_row_element = weights
1310 .quot_segments
1311 .dot(&Array1::from(quotient_segments_elements.to_vec()));
1312 profiler!(stop "main & aux elements");
1313
1314 profiler!(start "DEEP update");
1315 let main_and_aux_curr_row_deep_value = Stark::deep_update(
1316 current_fri_domain_value,
1317 main_and_aux_curr_row_element,
1318 out_of_domain_point_curr_row,
1319 out_of_domain_curr_row_main_and_aux_value,
1320 );
1321 let main_and_aux_next_row_deep_value = Stark::deep_update(
1322 current_fri_domain_value,
1323 main_and_aux_curr_row_element,
1324 out_of_domain_point_next_row,
1325 out_of_domain_next_row_main_and_aux_value,
1326 );
1327 let quot_curr_row_deep_value = Stark::deep_update(
1328 current_fri_domain_value,
1329 quotient_segments_curr_row_element,
1330 out_of_domain_point_curr_row_pow_num_segments,
1331 out_of_domain_curr_row_quotient_segment_value,
1332 );
1333 profiler!(stop "DEEP update");
1334
1335 profiler!(start "combination codeword equality");
1336 let deep_value_components = Array1::from(vec![
1337 main_and_aux_curr_row_deep_value,
1338 main_and_aux_next_row_deep_value,
1339 quot_curr_row_deep_value,
1340 ]);
1341 if fri_value != weights.deep.dot(&deep_value_components) {
1342 return Err(VerificationError::CombinationCodewordMismatch);
1343 };
1344 profiler!(stop "combination codeword equality");
1345 }
1346 profiler!(stop "linear combination");
1347 Ok(())
1348 }
1349
1350 fn hash_quotient_segment_elements(quotient_segment_rows: &[QuotientSegments]) -> Vec<Digest> {
1351 let interpret_xfe_as_bfes = |xfe: XFieldElement| xfe.coefficients.to_vec();
1352 let collect_row_as_bfes = |row: &QuotientSegments| row.map(interpret_xfe_as_bfes).concat();
1353 quotient_segment_rows
1354 .iter()
1355 .map(collect_row_as_bfes)
1356 .map(|row| Tip5::hash_varlen(&row))
1357 .collect()
1358 }
1359
1360 fn linearly_sum_main_and_aux_row<FF>(
1361 main_row: ArrayView1<FF>,
1362 aux_row: ArrayView1<XFieldElement>,
1363 weights: ArrayView1<XFieldElement>,
1364 ) -> XFieldElement
1365 where
1366 FF: FiniteField + Into<XFieldElement>,
1367 XFieldElement: Mul<FF, Output = XFieldElement>,
1368 {
1369 profiler!(start "collect");
1370 let mut row = main_row.map(|&element| element.into());
1371 row.append(Axis(0), aux_row).unwrap();
1372 profiler!(stop "collect");
1373 profiler!(start "inner product");
1374 let weights = weights.to_owned();
1378 let main_and_aux_element = (weights * row).sum();
1379 profiler!(stop "inner product");
1380 main_and_aux_element
1381 }
1382}
1383
1384impl Stark {
1385 pub fn new(security_level: usize, log2_of_fri_expansion_factor: usize) -> Self {
1389 assert_ne!(
1390 0, log2_of_fri_expansion_factor,
1391 "FRI expansion factor must be greater than one."
1392 );
1393
1394 let fri_expansion_factor = 1 << log2_of_fri_expansion_factor;
1395 let num_collinearity_checks = security_level / log2_of_fri_expansion_factor;
1396
1397 let num_out_of_domain_rows = 2;
1398 let num_trace_randomizers = num_collinearity_checks
1399 + num_out_of_domain_rows * x_field_element::EXTENSION_DEGREE
1400 + NUM_QUOTIENT_SEGMENTS * x_field_element::EXTENSION_DEGREE;
1401
1402 Stark {
1403 security_level,
1404 fri_expansion_factor,
1405 num_trace_randomizers,
1406 num_collinearity_checks,
1407 }
1408 }
1409
1410 pub fn prove(
1417 &self,
1418 claim: &Claim,
1419 aet: &AlgebraicExecutionTrace,
1420 ) -> Result<Proof, ProvingError> {
1421 Prover::new(*self).prove(claim, aet)
1422 }
1423
1424 pub fn verify(&self, claim: &Claim, proof: &Proof) -> Result<(), VerificationError> {
1429 Verifier::new(*self).verify(claim, proof)
1430 }
1431
1432 pub fn max_degree(&self, padded_height: usize) -> isize {
1436 let interpolant_degree = interpolant_degree(padded_height, self.num_trace_randomizers);
1437 let max_constraint_degree_with_origin =
1438 max_degree_with_origin(interpolant_degree, padded_height);
1439 let max_constraint_degree = max_constraint_degree_with_origin.degree as u64;
1440 let min_arithmetic_domain_length_supporting_max_constraint_degree =
1441 max_constraint_degree.next_power_of_two();
1442 let max_degree_supported_by_that_smallest_arithmetic_domain =
1443 min_arithmetic_domain_length_supporting_max_constraint_degree - 1;
1444
1445 max_degree_supported_by_that_smallest_arithmetic_domain as isize
1446 }
1447
1448 pub fn fri(&self, padded_height: usize) -> fri::SetupResult<Fri> {
1458 let fri_domain_length = self.fri_expansion_factor
1459 * randomized_trace_len(padded_height, self.num_trace_randomizers);
1460 let coset_offset = BFieldElement::generator();
1461 let domain = ArithmeticDomain::of_length(fri_domain_length)?.with_offset(coset_offset);
1462
1463 Fri::new(
1464 domain,
1465 self.fri_expansion_factor,
1466 self.num_collinearity_checks,
1467 )
1468 }
1469
1470 #[inline]
1474 fn deep_update(
1475 in_domain_point: BFieldElement,
1476 in_domain_value: XFieldElement,
1477 out_of_domain_point: XFieldElement,
1478 out_of_domain_value: XFieldElement,
1479 ) -> XFieldElement {
1480 (in_domain_value - out_of_domain_value) / (in_domain_point - out_of_domain_point)
1481 }
1482}
1483
1484impl Default for Stark {
1485 fn default() -> Self {
1486 let log_2_of_fri_expansion_factor = 2;
1487 let security_level = 160;
1488
1489 Self::new(security_level, log_2_of_fri_expansion_factor)
1490 }
1491}
1492
1493impl Default for Prover {
1494 fn default() -> Self {
1495 Self::new(Stark::default())
1496 }
1497}
1498
1499impl<'a> Arbitrary<'a> for Stark {
1500 fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
1501 let security_level = u.int_in_range(1..=640)?;
1502 let log_2_of_fri_expansion_factor = u.int_in_range(1..=8)?;
1503 Ok(Self::new(security_level, log_2_of_fri_expansion_factor))
1504 }
1505}
1506
1507struct LinearCombinationWeights {
1510 main: Array1<XFieldElement>,
1512
1513 aux: Array1<XFieldElement>,
1515
1516 quot_segments: Array1<XFieldElement>,
1518
1519 deep: Array1<XFieldElement>,
1521}
1522
1523impl LinearCombinationWeights {
1524 const NUM: usize = MasterMainTable::NUM_COLUMNS
1525 + MasterAuxTable::NUM_COLUMNS
1526 + NUM_QUOTIENT_SEGMENTS
1527 + NUM_DEEP_CODEWORD_COMPONENTS;
1528
1529 fn sample(proof_stream: &mut ProofStream) -> Self {
1530 const MAIN_END: usize = MasterMainTable::NUM_COLUMNS;
1531 const AUX_END: usize = MAIN_END + MasterAuxTable::NUM_COLUMNS;
1532 const QUOT_END: usize = AUX_END + NUM_QUOTIENT_SEGMENTS;
1533
1534 let weights = proof_stream.sample_scalars(Self::NUM);
1535
1536 Self {
1537 main: weights[..MAIN_END].to_vec().into(),
1538 aux: weights[MAIN_END..AUX_END].to_vec().into(),
1539 quot_segments: weights[AUX_END..QUOT_END].to_vec().into(),
1540 deep: weights[QUOT_END..].to_vec().into(),
1541 }
1542 }
1543
1544 fn main_and_aux(&self) -> Array1<XFieldElement> {
1545 let main = self.main.clone().into_iter();
1546 main.chain(self.aux.clone()).collect()
1547 }
1548}
1549
1550#[cfg(test)]
1551pub(crate) mod tests {
1552 use std::collections::HashMap;
1553 use std::collections::HashSet;
1554
1555 use air::challenge_id::ChallengeId::StandardInputIndeterminate;
1556 use air::challenge_id::ChallengeId::StandardOutputIndeterminate;
1557 use air::cross_table_argument::CrossTableArg;
1558 use air::cross_table_argument::EvalArg;
1559 use air::cross_table_argument::GrandCrossTableArg;
1560 use air::table::cascade::CascadeTable;
1561 use air::table::hash::HashTable;
1562 use air::table::jump_stack::JumpStackTable;
1563 use air::table::lookup::LookupTable;
1564 use air::table::op_stack::OpStackTable;
1565 use air::table::processor::ProcessorTable;
1566 use air::table::program::ProgramTable;
1567 use air::table::ram;
1568 use air::table::ram::RamTable;
1569 use air::table::u32::U32Table;
1570 use air::table::TableId;
1571 use air::table_column::MasterAuxColumn;
1572 use air::table_column::MasterMainColumn;
1573 use air::table_column::OpStackMainColumn;
1574 use air::table_column::ProcessorAuxColumn::InputTableEvalArg;
1575 use air::table_column::ProcessorAuxColumn::OutputTableEvalArg;
1576 use air::table_column::ProcessorMainColumn;
1577 use air::table_column::RamMainColumn;
1578 use air::AIR;
1579 use assert2::assert;
1580 use assert2::check;
1581 use assert2::let_assert;
1582 use constraint_circuit::ConstraintCircuitBuilder;
1583 use isa::error::OpStackError;
1584 use isa::instruction::Instruction;
1585 use isa::op_stack::OpStackElement;
1586 use itertools::izip;
1587 use proptest::collection::vec;
1588 use proptest::prelude::*;
1589 use proptest::test_runner::TestCaseResult;
1590 use proptest_arbitrary_interop::arb;
1591 use rand::prelude::*;
1592 use rand::Rng;
1593 use strum::EnumCount;
1594 use strum::IntoEnumIterator;
1595 use test_strategy::proptest;
1596 use twenty_first::math::other::random_elements;
1597
1598 use super::*;
1599 use crate::config::CacheDecision;
1600 use crate::error::InstructionError;
1601 use crate::shared_tests::construct_master_main_table;
1602 use crate::shared_tests::low_security_stark;
1603 use crate::shared_tests::prove_and_verify;
1604 use crate::shared_tests::ProgramAndInput;
1605 use crate::shared_tests::DEFAULT_LOG2_FRI_EXPANSION_FACTOR_FOR_TESTS;
1606 use crate::table::auxiliary_table;
1607 use crate::table::auxiliary_table::Evaluable;
1608 use crate::table::master_table::MasterAuxTable;
1609 use crate::triton_program;
1610 use crate::vm::tests::property_based_test_program_for_and;
1611 use crate::vm::tests::property_based_test_program_for_assert_vector;
1612 use crate::vm::tests::property_based_test_program_for_div_mod;
1613 use crate::vm::tests::property_based_test_program_for_eq;
1614 use crate::vm::tests::property_based_test_program_for_is_u32;
1615 use crate::vm::tests::property_based_test_program_for_log2floor;
1616 use crate::vm::tests::property_based_test_program_for_lsb;
1617 use crate::vm::tests::property_based_test_program_for_lt;
1618 use crate::vm::tests::property_based_test_program_for_pop_count;
1619 use crate::vm::tests::property_based_test_program_for_pow;
1620 use crate::vm::tests::property_based_test_program_for_random_ram_access;
1621 use crate::vm::tests::property_based_test_program_for_split;
1622 use crate::vm::tests::property_based_test_program_for_xb_dot_step;
1623 use crate::vm::tests::property_based_test_program_for_xor;
1624 use crate::vm::tests::property_based_test_program_for_xx_dot_step;
1625 use crate::vm::tests::test_program_0_lt_0;
1626 use crate::vm::tests::test_program_claim_in_ram_corresponds_to_currently_running_program;
1627 use crate::vm::tests::test_program_for_add_mul_invert;
1628 use crate::vm::tests::test_program_for_and;
1629 use crate::vm::tests::test_program_for_assert_vector;
1630 use crate::vm::tests::test_program_for_call_recurse_return;
1631 use crate::vm::tests::test_program_for_div_mod;
1632 use crate::vm::tests::test_program_for_divine;
1633 use crate::vm::tests::test_program_for_eq;
1634 use crate::vm::tests::test_program_for_halt;
1635 use crate::vm::tests::test_program_for_hash;
1636 use crate::vm::tests::test_program_for_log2floor;
1637 use crate::vm::tests::test_program_for_lsb;
1638 use crate::vm::tests::test_program_for_lt;
1639 use crate::vm::tests::test_program_for_many_sponge_instructions;
1640 use crate::vm::tests::test_program_for_merkle_step_left_sibling;
1641 use crate::vm::tests::test_program_for_merkle_step_mem_left_sibling;
1642 use crate::vm::tests::test_program_for_merkle_step_mem_right_sibling;
1643 use crate::vm::tests::test_program_for_merkle_step_right_sibling;
1644 use crate::vm::tests::test_program_for_pop_count;
1645 use crate::vm::tests::test_program_for_pow;
1646 use crate::vm::tests::test_program_for_push_pop_dup_swap_nop;
1647 use crate::vm::tests::test_program_for_read_io_write_io;
1648 use crate::vm::tests::test_program_for_recurse_or_return;
1649 use crate::vm::tests::test_program_for_skiz;
1650 use crate::vm::tests::test_program_for_split;
1651 use crate::vm::tests::test_program_for_sponge_instructions;
1652 use crate::vm::tests::test_program_for_sponge_instructions_2;
1653 use crate::vm::tests::test_program_for_starting_with_pop_count;
1654 use crate::vm::tests::test_program_for_write_mem_read_mem;
1655 use crate::vm::tests::test_program_for_x_invert;
1656 use crate::vm::tests::test_program_for_xb_mul;
1657 use crate::vm::tests::test_program_for_xor;
1658 use crate::vm::tests::test_program_for_xx_add;
1659 use crate::vm::tests::test_program_for_xx_mul;
1660 use crate::vm::tests::test_program_hash_nop_nop_lt;
1661 use crate::vm::tests::ProgramForMerkleTreeUpdate;
1662 use crate::vm::tests::ProgramForRecurseOrReturn;
1663 use crate::vm::tests::ProgramForSpongeAndHashInstructions;
1664 use crate::vm::NonDeterminism;
1665 use crate::vm::VM;
1666 use crate::PublicInput;
1667
1668 pub(crate) fn master_main_table_for_low_security_level(
1669 program_and_input: ProgramAndInput,
1670 ) -> (Stark, Claim, MasterMainTable) {
1671 let ProgramAndInput {
1672 program,
1673 public_input,
1674 non_determinism,
1675 } = program_and_input;
1676
1677 let claim = Claim::about_program(&program).with_input(public_input.clone());
1678 let (aet, stdout) = VM::trace_execution(program, public_input, non_determinism).unwrap();
1679 let claim = claim.with_output(stdout);
1680
1681 let stark = low_security_stark(DEFAULT_LOG2_FRI_EXPANSION_FACTOR_FOR_TESTS);
1682 let master_main_table = construct_master_main_table(stark, &aet);
1683
1684 (stark, claim, master_main_table)
1685 }
1686
1687 pub(crate) fn master_tables_for_low_security_level(
1688 program_and_input: ProgramAndInput,
1689 ) -> (Stark, Claim, MasterMainTable, MasterAuxTable, Challenges) {
1690 let (stark, claim, mut master_main_table) =
1691 master_main_table_for_low_security_level(program_and_input);
1692
1693 let challenges = Challenges::placeholder(&claim);
1694 master_main_table.pad();
1695 let master_aux_table = master_main_table.extend(&challenges);
1696
1697 (
1698 stark,
1699 claim,
1700 master_main_table,
1701 master_aux_table,
1702 challenges,
1703 )
1704 }
1705
1706 #[proptest]
1707 fn two_default_provers_have_different_randomness_seeds() {
1708 let seed = || Prover::default().randomness_seed;
1709 prop_assert_ne!(seed(), seed());
1710 }
1711
1712 #[test]
1713 fn quotient_segments_are_independent_of_fri_table_caching() {
1714 crate::config::overwrite_lde_trace_caching_to(CacheDecision::Cache);
1716
1717 let mut rng = StdRng::seed_from_u64(1632525295622789151);
1718 let weights = rng.random::<[XFieldElement; MasterAuxTable::NUM_CONSTRAINTS]>();
1719
1720 let program = ProgramAndInput::new(triton_program!(halt));
1721 let (stark, _, mut main, mut aux, ch) = master_tables_for_low_security_level(program);
1722 let padded_height = main.trace_table().nrows();
1723 let fri_dom = stark.fri(padded_height).unwrap().domain;
1724 let max_degree = stark.max_degree(padded_height);
1725 let quot_dom = Prover::quotient_domain(fri_dom, max_degree).unwrap();
1726
1727 debug_assert!(main.fri_domain_table().is_none());
1728 debug_assert!(aux.fri_domain_table().is_none());
1729 let jit_segments =
1730 Prover::compute_quotient_segments(&mut main, &mut aux, quot_dom, &ch, &weights);
1731
1732 debug_assert!(main.fri_domain_table().is_none());
1733 main.maybe_low_degree_extend_all_columns();
1734 debug_assert!(main.fri_domain_table().is_some());
1735
1736 debug_assert!(aux.fri_domain_table().is_none());
1737 aux.maybe_low_degree_extend_all_columns();
1738 debug_assert!(aux.fri_domain_table().is_some());
1739
1740 let cache_segments =
1741 Prover::compute_quotient_segments(&mut main, &mut aux, quot_dom, &ch, &weights);
1742
1743 assert_eq!(jit_segments, cache_segments);
1744 }
1745
1746 #[test]
1750 fn computing_quotient_segments_does_not_change_execution_trace() {
1751 fn assert_no_trace_mutation(cache_decision: CacheDecision) {
1752 crate::config::overwrite_lde_trace_caching_to(cache_decision);
1753
1754 let mut rng = StdRng::seed_from_u64(15157673430940347283);
1755 let weights = rng.random::<[XFieldElement; MasterAuxTable::NUM_CONSTRAINTS]>();
1756
1757 let program = ProgramAndInput::new(triton_program!(halt));
1758 let (stark, _, mut main, mut aux, ch) = master_tables_for_low_security_level(program);
1759
1760 let original_main_trace = main.trace_table().to_owned();
1761 let original_aux_trace = aux.trace_table().to_owned();
1762
1763 let padded_height = main.trace_table().nrows();
1764 let fri_dom = stark.fri(padded_height).unwrap().domain;
1765 let max_degree = stark.max_degree(padded_height);
1766 let quot_dom = Prover::quotient_domain(fri_dom, max_degree).unwrap();
1767
1768 if cache_decision == CacheDecision::Cache {
1769 main.maybe_low_degree_extend_all_columns();
1770 assert!(main.fri_domain_table().is_some());
1771
1772 aux.maybe_low_degree_extend_all_columns();
1773 assert!(aux.fri_domain_table().is_some());
1774 }
1775
1776 let _segments =
1777 Prover::compute_quotient_segments(&mut main, &mut aux, quot_dom, &ch, &weights);
1778
1779 assert_eq!(original_main_trace, main.trace_table());
1780 assert_eq!(original_aux_trace, aux.trace_table());
1781 }
1782
1783 assert_no_trace_mutation(CacheDecision::Cache);
1784 assert_no_trace_mutation(CacheDecision::NoCache);
1785 }
1786
1787 #[test]
1788 fn supplying_prover_randomness_seed_fully_derandomizes_produced_proof() {
1789 let ProgramAndInput {
1790 program,
1791 public_input,
1792 non_determinism,
1793 } = program_executing_every_instruction();
1794
1795 let claim = Claim::about_program(&program).with_input(public_input.clone());
1796 let (aet, output) = VM::trace_execution(program, public_input, non_determinism).unwrap();
1797 let claim = claim.with_output(output);
1798
1799 let stark = low_security_stark(DEFAULT_LOG2_FRI_EXPANSION_FACTOR_FOR_TESTS);
1800 let mut rng = StdRng::seed_from_u64(3351975627407608972);
1801 let proof = Prover::new(stark)
1802 .set_randomness_seed_which_may_break_zero_knowledge(rng.random())
1803 .prove(&claim, &aet)
1804 .unwrap();
1805
1806 insta::assert_snapshot!(
1807 Tip5::hash(&proof),
1808 @"17275651906185656762,\
1809 13250937299792022858,\
1810 05731754925513787901,\
1811 05512095638892086027,\
1812 08634562101877660478",
1813 );
1814 }
1815
1816 #[test]
1817 fn print_ram_table_example_for_specification() {
1818 let program = triton_program!(
1819 push 20 push 100 write_mem 1 pop 1 push 5 push 6 push 7 push 8 push 9
1821 push 42 write_mem 5 pop 1 push 42 read_mem 1 pop 2 push 45 read_mem 3 pop 4 push 17 push 18 push 19
1825 push 43 write_mem 3 pop 1 push 46 read_mem 5 pop 1 pop 5 push 42 read_mem 1 pop 2 push 100 read_mem 1 pop 2 halt
1830 );
1831 let (_, _, master_main_table, _, _) =
1832 master_tables_for_low_security_level(ProgramAndInput::new(program));
1833
1834 println!();
1835 println!("Processor Table:\n");
1836 println!("| clk | ci | nia | st0 | st1 | st2 | st3 | st4 | st5 |");
1837 println!("|----:|:----|:----|----:|----:|----:|----:|----:|----:|");
1838 for row in master_main_table
1839 .table(TableId::Processor)
1840 .rows()
1841 .into_iter()
1842 .take(40)
1843 {
1844 let clk = row[ProcessorMainColumn::CLK.main_index()].to_string();
1845 let st0 = row[ProcessorMainColumn::ST0.main_index()].to_string();
1846 let st1 = row[ProcessorMainColumn::ST1.main_index()].to_string();
1847 let st2 = row[ProcessorMainColumn::ST2.main_index()].to_string();
1848 let st3 = row[ProcessorMainColumn::ST3.main_index()].to_string();
1849 let st4 = row[ProcessorMainColumn::ST4.main_index()].to_string();
1850 let st5 = row[ProcessorMainColumn::ST5.main_index()].to_string();
1851
1852 let (ci, nia) = ci_and_nia_from_master_table_row(row);
1853
1854 let interesting_cols = [clk, ci, nia, st0, st1, st2, st3, st4, st5];
1855 let interesting_cols = interesting_cols
1856 .iter()
1857 .map(|ff| format!("{:>10}", format!("{ff}")))
1858 .join(" | ");
1859 println!("| {interesting_cols} |");
1860 }
1861 println!();
1862 println!("RAM Table:\n");
1863 println!("| clk | type | pointer | value | iord |");
1864 println!("|----:|:-----|--------:|------:|-----:|");
1865 for row in master_main_table
1866 .table(TableId::Ram)
1867 .rows()
1868 .into_iter()
1869 .take(25)
1870 {
1871 let clk = row[RamMainColumn::CLK.main_index()].to_string();
1872 let ramp = row[RamMainColumn::RamPointer.main_index()].to_string();
1873 let ramv = row[RamMainColumn::RamValue.main_index()].to_string();
1874 let iord = row[RamMainColumn::InverseOfRampDifference.main_index()].to_string();
1875
1876 let instruction_type = match row[RamMainColumn::InstructionType.main_index()] {
1877 ram::INSTRUCTION_TYPE_READ => "read",
1878 ram::INSTRUCTION_TYPE_WRITE => "write",
1879 ram::PADDING_INDICATOR => "pad",
1880 _ => "-",
1881 }
1882 .to_string();
1883
1884 let interesting_cols = [clk, instruction_type, ramp, ramv, iord];
1885 let interesting_cols = interesting_cols
1886 .iter()
1887 .map(|ff| format!("{:>10}", format!("{ff}")))
1888 .join(" | ");
1889 println!("| {interesting_cols} |");
1890 }
1891 }
1892
1893 #[test]
1894 fn print_op_stack_table_example_for_specification() {
1895 let num_interesting_rows = 30;
1896 let fake_op_stack_size = 4;
1897
1898 let program = triton_program! {
1899 push 42 push 43 push 44 push 45 push 46 push 47 push 48
1900 nop pop 1 pop 2 pop 1
1901 push 77 swap 3 push 78 swap 3 push 79
1902 pop 1 pop 2 pop 3
1903 halt
1904 };
1905 let (_, _, master_main_table) =
1906 master_main_table_for_low_security_level(ProgramAndInput::new(program));
1907
1908 println!();
1909 println!("Processor Table:");
1910 println!("| clk | ci | nia | st0 | st1 | st2 | st3 | underflow | pointer |");
1911 println!("|----:|:----|----:|----:|----:|----:|----:|:----------|--------:|");
1912 for row in master_main_table
1913 .table(TableId::Processor)
1914 .rows()
1915 .into_iter()
1916 .take(num_interesting_rows)
1917 {
1918 let clk = row[ProcessorMainColumn::CLK.main_index()].to_string();
1919 let st0 = row[ProcessorMainColumn::ST0.main_index()].to_string();
1920 let st1 = row[ProcessorMainColumn::ST1.main_index()].to_string();
1921 let st2 = row[ProcessorMainColumn::ST2.main_index()].to_string();
1922 let st3 = row[ProcessorMainColumn::ST3.main_index()].to_string();
1923 let st4 = row[ProcessorMainColumn::ST4.main_index()].to_string();
1924 let st5 = row[ProcessorMainColumn::ST5.main_index()].to_string();
1925 let st6 = row[ProcessorMainColumn::ST6.main_index()].to_string();
1926 let st7 = row[ProcessorMainColumn::ST7.main_index()].to_string();
1927 let st8 = row[ProcessorMainColumn::ST8.main_index()].to_string();
1928 let st9 = row[ProcessorMainColumn::ST9.main_index()].to_string();
1929
1930 let osp = row[ProcessorMainColumn::OpStackPointer.main_index()];
1931 let osp =
1932 (osp.value() + fake_op_stack_size).saturating_sub(OpStackElement::COUNT as u64);
1933
1934 let underflow_size = osp.saturating_sub(fake_op_stack_size);
1935 let underflow_candidates = [st4, st5, st6, st7, st8, st9];
1936 let underflow = underflow_candidates
1937 .into_iter()
1938 .take(underflow_size as usize);
1939 let underflow = underflow.map(|ff| format!("{:>2}", format!("{ff}")));
1940 let underflow = format!("[{}]", underflow.collect_vec().join(", "));
1941
1942 let osp = osp.to_string();
1943 let (ci, nia) = ci_and_nia_from_master_table_row(row);
1944
1945 let interesting_cols = [clk, ci, nia, st0, st1, st2, st3, underflow, osp];
1946 let interesting_cols = interesting_cols
1947 .map(|ff| format!("{:>10}", format!("{ff}")))
1948 .join(" | ");
1949 println!("| {interesting_cols} |");
1950 }
1951
1952 println!();
1953 println!("Op Stack Table:");
1954 println!("| clk | ib1 | pointer | value |");
1955 println!("|----:|----:|--------:|------:|");
1956 for row in master_main_table
1957 .table(TableId::OpStack)
1958 .rows()
1959 .into_iter()
1960 .take(num_interesting_rows)
1961 {
1962 let clk = row[OpStackMainColumn::CLK.main_index()].to_string();
1963 let ib1 = row[OpStackMainColumn::IB1ShrinkStack.main_index()].to_string();
1964
1965 let osp = row[OpStackMainColumn::StackPointer.main_index()];
1966 let osp =
1967 (osp.value() + fake_op_stack_size).saturating_sub(OpStackElement::COUNT as u64);
1968 let osp = osp.to_string();
1969
1970 let value = row[OpStackMainColumn::FirstUnderflowElement.main_index()].to_string();
1971
1972 let interesting_cols = [clk, ib1, osp, value];
1973 let interesting_cols = interesting_cols
1974 .map(|ff| format!("{:>10}", format!("{ff}")))
1975 .join(" | ");
1976 println!("| {interesting_cols} |");
1977 }
1978 }
1979
1980 fn ci_and_nia_from_master_table_row(row: ArrayView1<BFieldElement>) -> (String, String) {
1981 let curr_instruction = row[ProcessorMainColumn::CI.main_index()].value();
1982 let next_instruction_or_arg = row[ProcessorMainColumn::NIA.main_index()].value();
1983
1984 let curr_instruction = Instruction::try_from(curr_instruction).unwrap();
1985 let nia = curr_instruction
1986 .arg()
1987 .map(|_| next_instruction_or_arg.to_string())
1988 .unwrap_or_default();
1989 (curr_instruction.name().to_string(), nia)
1990 }
1991
1992 #[test]
1994 fn print_all_constraint_degrees() {
1995 let padded_height = 2;
1996 let num_trace_randomizers = 2;
1997 let interpolant_degree = interpolant_degree(padded_height, num_trace_randomizers);
1998 for deg in auxiliary_table::all_degrees_with_origin(interpolant_degree, padded_height) {
1999 println!("{deg}");
2000 }
2001 }
2002
2003 #[test]
2004 fn check_io_terminals() {
2005 let read_nop_program = triton_program!(
2006 read_io 3 nop nop write_io 2 push 17 write_io 1 halt
2007 );
2008 let mut program_and_input = ProgramAndInput::new(read_nop_program);
2009 program_and_input.public_input = PublicInput::from([3, 5, 7].map(|b| bfe!(b)));
2010 let (_, claim, _, master_aux_table, all_challenges) =
2011 master_tables_for_low_security_level(program_and_input);
2012
2013 let processor_table = master_aux_table.table(TableId::Processor);
2014 let processor_table_last_row = processor_table.slice(s![-1, ..]);
2015 let ptie = processor_table_last_row[InputTableEvalArg.aux_index()];
2016 let ine = EvalArg::compute_terminal(
2017 &claim.input,
2018 EvalArg::default_initial(),
2019 all_challenges[StandardInputIndeterminate],
2020 );
2021 check!(ptie == ine);
2022
2023 let ptoe = processor_table_last_row[OutputTableEvalArg.aux_index()];
2024 let oute = EvalArg::compute_terminal(
2025 &claim.output,
2026 EvalArg::default_initial(),
2027 all_challenges[StandardOutputIndeterminate],
2028 );
2029 check!(ptoe == oute);
2030 }
2031
2032 #[test]
2033 fn constraint_polynomials_use_right_number_of_variables() {
2034 let challenges = Challenges::default();
2035 let main_row = Array1::<BFieldElement>::zeros(MasterMainTable::NUM_COLUMNS);
2036 let aux_row = Array1::zeros(MasterAuxTable::NUM_COLUMNS);
2037
2038 let br = main_row.view();
2039 let er = aux_row.view();
2040
2041 MasterAuxTable::evaluate_initial_constraints(br, er, &challenges);
2042 MasterAuxTable::evaluate_consistency_constraints(br, er, &challenges);
2043 MasterAuxTable::evaluate_transition_constraints(br, er, br, er, &challenges);
2044 MasterAuxTable::evaluate_terminal_constraints(br, er, &challenges);
2045 }
2046
2047 #[test]
2048 fn print_number_of_all_constraints_per_table() {
2049 let table_names = [
2050 "program table",
2051 "processor table",
2052 "op stack table",
2053 "ram table",
2054 "jump stack table",
2055 "hash table",
2056 "cascade table",
2057 "lookup table",
2058 "u32 table",
2059 "cross-table arg",
2060 ];
2061 let circuit_builder = ConstraintCircuitBuilder::new();
2062 let all_init = [
2063 ProgramTable::initial_constraints(&circuit_builder),
2064 ProcessorTable::initial_constraints(&circuit_builder),
2065 OpStackTable::initial_constraints(&circuit_builder),
2066 RamTable::initial_constraints(&circuit_builder),
2067 JumpStackTable::initial_constraints(&circuit_builder),
2068 HashTable::initial_constraints(&circuit_builder),
2069 CascadeTable::initial_constraints(&circuit_builder),
2070 LookupTable::initial_constraints(&circuit_builder),
2071 U32Table::initial_constraints(&circuit_builder),
2072 GrandCrossTableArg::initial_constraints(&circuit_builder),
2073 ]
2074 .map(|vec| vec.len());
2075 let circuit_builder = ConstraintCircuitBuilder::new();
2076 let all_cons = [
2077 ProgramTable::consistency_constraints(&circuit_builder),
2078 ProcessorTable::consistency_constraints(&circuit_builder),
2079 OpStackTable::consistency_constraints(&circuit_builder),
2080 RamTable::consistency_constraints(&circuit_builder),
2081 JumpStackTable::consistency_constraints(&circuit_builder),
2082 HashTable::consistency_constraints(&circuit_builder),
2083 CascadeTable::consistency_constraints(&circuit_builder),
2084 LookupTable::consistency_constraints(&circuit_builder),
2085 U32Table::consistency_constraints(&circuit_builder),
2086 GrandCrossTableArg::consistency_constraints(&circuit_builder),
2087 ]
2088 .map(|vec| vec.len());
2089 let circuit_builder = ConstraintCircuitBuilder::new();
2090 let all_trans = [
2091 ProgramTable::transition_constraints(&circuit_builder),
2092 ProcessorTable::transition_constraints(&circuit_builder),
2093 OpStackTable::transition_constraints(&circuit_builder),
2094 RamTable::transition_constraints(&circuit_builder),
2095 JumpStackTable::transition_constraints(&circuit_builder),
2096 HashTable::transition_constraints(&circuit_builder),
2097 CascadeTable::transition_constraints(&circuit_builder),
2098 LookupTable::transition_constraints(&circuit_builder),
2099 U32Table::transition_constraints(&circuit_builder),
2100 GrandCrossTableArg::transition_constraints(&circuit_builder),
2101 ]
2102 .map(|vec| vec.len());
2103 let circuit_builder = ConstraintCircuitBuilder::new();
2104 let all_term = [
2105 ProgramTable::terminal_constraints(&circuit_builder),
2106 ProcessorTable::terminal_constraints(&circuit_builder),
2107 OpStackTable::terminal_constraints(&circuit_builder),
2108 RamTable::terminal_constraints(&circuit_builder),
2109 JumpStackTable::terminal_constraints(&circuit_builder),
2110 HashTable::terminal_constraints(&circuit_builder),
2111 CascadeTable::terminal_constraints(&circuit_builder),
2112 LookupTable::terminal_constraints(&circuit_builder),
2113 U32Table::terminal_constraints(&circuit_builder),
2114 GrandCrossTableArg::terminal_constraints(&circuit_builder),
2115 ]
2116 .map(|vec| vec.len());
2117
2118 let num_total_init: usize = all_init.iter().sum();
2119 let num_total_cons: usize = all_cons.iter().sum();
2120 let num_total_trans: usize = all_trans.iter().sum();
2121 let num_total_term: usize = all_term.iter().sum();
2122 let num_total = num_total_init + num_total_cons + num_total_trans + num_total_term;
2123
2124 println!();
2125 println!("| Table | Init | Cons | Trans | Term | Sum |");
2126 println!("|:---------------------|------:|------:|------:|------:|------:|");
2127 for (name, num_init, num_cons, num_trans, num_term) in
2128 izip!(table_names, all_init, all_cons, all_trans, all_term)
2129 {
2130 let num_total = num_init + num_cons + num_trans + num_term;
2131 println!(
2132 "| {name:<20} | {num_init:>5} | {num_cons:>5} \
2133 | {num_trans:>5} | {num_term:>5} | {num_total:>5} |",
2134 );
2135 }
2136 println!(
2137 "| {:<20} | {num_total_init:>5} | {num_total_cons:>5} \
2138 | {num_total_trans:>5} | {num_total_term:>5} | {num_total:>5} |",
2139 "Sum",
2140 );
2141 }
2142
2143 #[test]
2144 fn number_of_quotient_degree_bounds_match_number_of_constraints() {
2145 let main_row = Array1::<BFieldElement>::zeros(MasterMainTable::NUM_COLUMNS);
2146 let aux_row = Array1::zeros(MasterAuxTable::NUM_COLUMNS);
2147 let ch = Challenges::default();
2148 let padded_height = 2;
2149 let num_trace_randomizers = 2;
2150 let interpolant_degree = interpolant_degree(padded_height, num_trace_randomizers);
2151
2152 let ph = padded_height;
2154 let id = interpolant_degree;
2155 let br = main_row.view();
2156 let er = aux_row.view();
2157
2158 let num_init_quots = MasterAuxTable::NUM_INITIAL_CONSTRAINTS;
2159 let num_cons_quots = MasterAuxTable::NUM_CONSISTENCY_CONSTRAINTS;
2160 let num_tran_quots = MasterAuxTable::NUM_TRANSITION_CONSTRAINTS;
2161 let num_term_quots = MasterAuxTable::NUM_TERMINAL_CONSTRAINTS;
2162
2163 let eval_init_consts = MasterAuxTable::evaluate_initial_constraints(br, er, &ch);
2164 let eval_cons_consts = MasterAuxTable::evaluate_consistency_constraints(br, er, &ch);
2165 let eval_tran_consts = MasterAuxTable::evaluate_transition_constraints(br, er, br, er, &ch);
2166 let eval_term_consts = MasterAuxTable::evaluate_terminal_constraints(br, er, &ch);
2167
2168 assert!(num_init_quots == eval_init_consts.len());
2169 assert!(num_cons_quots == eval_cons_consts.len());
2170 assert!(num_tran_quots == eval_tran_consts.len());
2171 assert!(num_term_quots == eval_term_consts.len());
2172
2173 assert!(num_init_quots == MasterAuxTable::initial_quotient_degree_bounds(id).len());
2174 assert!(num_cons_quots == MasterAuxTable::consistency_quotient_degree_bounds(id, ph).len());
2175 assert!(num_tran_quots == MasterAuxTable::transition_quotient_degree_bounds(id, ph).len());
2176 assert!(num_term_quots == MasterAuxTable::terminal_quotient_degree_bounds(id).len());
2177 }
2178
2179 #[test]
2180 fn constraints_evaluate_to_zero_on_fibonacci() {
2181 let source_code_and_input =
2182 ProgramAndInput::new(crate::example_programs::FIBONACCI_SEQUENCE.clone())
2183 .with_input(bfe_array![100]);
2184 triton_constraints_evaluate_to_zero(source_code_and_input);
2185 }
2186
2187 #[test]
2188 fn constraints_evaluate_to_zero_on_big_mmr_snippet() {
2189 let source_code_and_input = ProgramAndInput::new(
2190 crate::example_programs::CALCULATE_NEW_MMR_PEAKS_FROM_APPEND_WITH_SAFE_LISTS.clone(),
2191 );
2192 triton_constraints_evaluate_to_zero(source_code_and_input);
2193 }
2194
2195 #[test]
2196 fn constraints_evaluate_to_zero_on_program_for_halt() {
2197 triton_constraints_evaluate_to_zero(test_program_for_halt())
2198 }
2199
2200 #[test]
2201 fn constraints_evaluate_to_zero_on_program_hash_nop_nop_lt() {
2202 triton_constraints_evaluate_to_zero(test_program_hash_nop_nop_lt())
2203 }
2204
2205 #[test]
2206 fn constraints_evaluate_to_zero_on_program_for_push_pop_dup_swap_nop() {
2207 triton_constraints_evaluate_to_zero(test_program_for_push_pop_dup_swap_nop())
2208 }
2209
2210 #[test]
2211 fn constraints_evaluate_to_zero_on_program_for_divine() {
2212 triton_constraints_evaluate_to_zero(test_program_for_divine())
2213 }
2214
2215 #[test]
2216 fn constraints_evaluate_to_zero_on_program_for_skiz() {
2217 triton_constraints_evaluate_to_zero(test_program_for_skiz())
2218 }
2219
2220 #[test]
2221 fn constraints_evaluate_to_zero_on_program_for_call_recurse_return() {
2222 triton_constraints_evaluate_to_zero(test_program_for_call_recurse_return())
2223 }
2224
2225 #[test]
2226 fn constraints_evaluate_to_zero_on_program_for_recurse_or_return() {
2227 triton_constraints_evaluate_to_zero(test_program_for_recurse_or_return())
2228 }
2229
2230 #[proptest(cases = 20)]
2231 fn constraints_evaluate_to_zero_on_property_based_test_program_for_recurse_or_return(
2232 program: ProgramForRecurseOrReturn,
2233 ) {
2234 triton_constraints_evaluate_to_zero(program.assemble())
2235 }
2236
2237 #[test]
2238 fn constraints_evaluate_to_zero_on_program_for_write_mem_read_mem() {
2239 triton_constraints_evaluate_to_zero(test_program_for_write_mem_read_mem())
2240 }
2241
2242 #[test]
2243 fn constraints_evaluate_to_zero_on_program_for_hash() {
2244 triton_constraints_evaluate_to_zero(test_program_for_hash())
2245 }
2246
2247 #[test]
2248 fn constraints_evaluate_to_zero_on_program_for_merkle_step_right_sibling() {
2249 triton_constraints_evaluate_to_zero(test_program_for_merkle_step_right_sibling())
2250 }
2251
2252 #[test]
2253 fn constraints_evaluate_to_zero_on_program_for_merkle_step_left_sibling() {
2254 triton_constraints_evaluate_to_zero(test_program_for_merkle_step_left_sibling())
2255 }
2256
2257 #[test]
2258 fn constraints_evaluate_to_zero_on_program_for_merkle_step_mem_right_sibling() {
2259 triton_constraints_evaluate_to_zero(test_program_for_merkle_step_mem_right_sibling())
2260 }
2261
2262 #[test]
2263 fn constraints_evaluate_to_zero_on_program_for_merkle_step_mem_left_sibling() {
2264 triton_constraints_evaluate_to_zero(test_program_for_merkle_step_mem_left_sibling())
2265 }
2266
2267 #[proptest(cases = 20)]
2268 fn constraints_evaluate_to_zero_on_property_based_test_program_for_merkle_tree_update(
2269 program: ProgramForMerkleTreeUpdate,
2270 ) {
2271 triton_constraints_evaluate_to_zero(program.assemble())
2272 }
2273
2274 #[test]
2275 fn constraints_evaluate_to_zero_on_program_for_assert_vector() {
2276 triton_constraints_evaluate_to_zero(test_program_for_assert_vector())
2277 }
2278
2279 #[test]
2280 fn constraints_evaluate_to_zero_on_program_for_sponge_instructions() {
2281 triton_constraints_evaluate_to_zero(test_program_for_sponge_instructions())
2282 }
2283
2284 #[test]
2285 fn constraints_evaluate_to_zero_on_program_for_sponge_instructions_2() {
2286 triton_constraints_evaluate_to_zero(test_program_for_sponge_instructions_2())
2287 }
2288
2289 #[test]
2290 fn constraints_evaluate_to_zero_on_program_for_many_sponge_instructions() {
2291 triton_constraints_evaluate_to_zero(test_program_for_many_sponge_instructions())
2292 }
2293
2294 #[test]
2295 fn constraints_evaluate_to_zero_on_program_for_add_mul_invert() {
2296 triton_constraints_evaluate_to_zero(test_program_for_add_mul_invert())
2297 }
2298
2299 #[test]
2300 fn constraints_evaluate_to_zero_on_program_for_eq() {
2301 triton_constraints_evaluate_to_zero(test_program_for_eq())
2302 }
2303
2304 #[test]
2305 fn constraints_evaluate_to_zero_on_program_for_lsb() {
2306 triton_constraints_evaluate_to_zero(test_program_for_lsb())
2307 }
2308
2309 #[test]
2310 fn constraints_evaluate_to_zero_on_program_for_split() {
2311 triton_constraints_evaluate_to_zero(test_program_for_split())
2312 }
2313
2314 #[test]
2315 fn constraints_evaluate_to_zero_on_program_0_lt_0() {
2316 triton_constraints_evaluate_to_zero(test_program_0_lt_0())
2317 }
2318
2319 #[test]
2320 fn constraints_evaluate_to_zero_on_program_for_lt() {
2321 triton_constraints_evaluate_to_zero(test_program_for_lt())
2322 }
2323
2324 #[test]
2325 fn constraints_evaluate_to_zero_on_program_for_and() {
2326 triton_constraints_evaluate_to_zero(test_program_for_and())
2327 }
2328
2329 #[test]
2330 fn constraints_evaluate_to_zero_on_program_for_xor() {
2331 triton_constraints_evaluate_to_zero(test_program_for_xor())
2332 }
2333
2334 #[test]
2335 fn constraints_evaluate_to_zero_on_program_for_log2floor() {
2336 triton_constraints_evaluate_to_zero(test_program_for_log2floor())
2337 }
2338
2339 #[test]
2340 fn constraints_evaluate_to_zero_on_program_for_pow() {
2341 triton_constraints_evaluate_to_zero(test_program_for_pow())
2342 }
2343
2344 #[test]
2345 fn constraints_evaluate_to_zero_on_program_for_div_mod() {
2346 triton_constraints_evaluate_to_zero(test_program_for_div_mod())
2347 }
2348
2349 #[test]
2350 fn constraints_evaluate_to_zero_on_program_for_starting_with_pop_count() {
2351 triton_constraints_evaluate_to_zero(test_program_for_starting_with_pop_count())
2352 }
2353
2354 #[test]
2355 fn constraints_evaluate_to_zero_on_program_for_pop_count() {
2356 triton_constraints_evaluate_to_zero(test_program_for_pop_count())
2357 }
2358
2359 #[test]
2360 fn constraints_evaluate_to_zero_on_program_for_xx_add() {
2361 triton_constraints_evaluate_to_zero(test_program_for_xx_add())
2362 }
2363
2364 #[test]
2365 fn constraints_evaluate_to_zero_on_program_for_xx_mul() {
2366 triton_constraints_evaluate_to_zero(test_program_for_xx_mul())
2367 }
2368
2369 #[test]
2370 fn constraints_evaluate_to_zero_on_program_for_x_invert() {
2371 triton_constraints_evaluate_to_zero(test_program_for_x_invert())
2372 }
2373
2374 #[test]
2375 fn constraints_evaluate_to_zero_on_program_for_xb_mul() {
2376 triton_constraints_evaluate_to_zero(test_program_for_xb_mul())
2377 }
2378
2379 #[test]
2380 fn constraints_evaluate_to_zero_on_program_for_read_io_write_io() {
2381 triton_constraints_evaluate_to_zero(test_program_for_read_io_write_io())
2382 }
2383
2384 #[test]
2385 fn constraints_evaluate_to_zero_on_property_based_test_program_for_assert_vector() {
2386 triton_constraints_evaluate_to_zero(property_based_test_program_for_assert_vector())
2387 }
2388
2389 #[test]
2390 fn constraints_evaluate_to_zero_on_single_sponge_absorb_mem_instructions() {
2391 let program = triton_program!(sponge_init sponge_absorb_mem halt);
2392 let program = ProgramAndInput::new(program);
2393 triton_constraints_evaluate_to_zero(program);
2394 }
2395
2396 #[proptest(cases = 3)]
2397 fn constraints_evaluate_to_zero_on_property_based_test_program_for_sponge_instructions(
2398 program: ProgramForSpongeAndHashInstructions,
2399 ) {
2400 triton_constraints_evaluate_to_zero(program.assemble());
2401 }
2402
2403 #[test]
2404 fn constraints_evaluate_to_zero_on_property_based_test_program_for_split() {
2405 triton_constraints_evaluate_to_zero(property_based_test_program_for_split())
2406 }
2407
2408 #[test]
2409 fn constraints_evaluate_to_zero_on_property_based_test_program_for_eq() {
2410 triton_constraints_evaluate_to_zero(property_based_test_program_for_eq())
2411 }
2412
2413 #[test]
2414 fn constraints_evaluate_to_zero_on_property_based_test_program_for_lsb() {
2415 triton_constraints_evaluate_to_zero(property_based_test_program_for_lsb())
2416 }
2417
2418 #[test]
2419 fn constraints_evaluate_to_zero_on_property_based_test_program_for_lt() {
2420 triton_constraints_evaluate_to_zero(property_based_test_program_for_lt())
2421 }
2422
2423 #[test]
2424 fn constraints_evaluate_to_zero_on_property_based_test_program_for_and() {
2425 triton_constraints_evaluate_to_zero(property_based_test_program_for_and())
2426 }
2427
2428 #[test]
2429 fn constraints_evaluate_to_zero_on_property_based_test_program_for_xor() {
2430 triton_constraints_evaluate_to_zero(property_based_test_program_for_xor())
2431 }
2432
2433 #[test]
2434 fn constraints_evaluate_to_zero_on_property_based_test_program_for_log2floor() {
2435 triton_constraints_evaluate_to_zero(property_based_test_program_for_log2floor())
2436 }
2437
2438 #[test]
2439 fn constraints_evaluate_to_zero_on_property_based_test_program_for_pow() {
2440 triton_constraints_evaluate_to_zero(property_based_test_program_for_pow())
2441 }
2442
2443 #[test]
2444 fn constraints_evaluate_to_zero_on_property_based_test_program_for_div_mod() {
2445 triton_constraints_evaluate_to_zero(property_based_test_program_for_div_mod())
2446 }
2447
2448 #[test]
2449 fn constraints_evaluate_to_zero_on_property_based_test_program_for_pop_count() {
2450 triton_constraints_evaluate_to_zero(property_based_test_program_for_pop_count())
2451 }
2452
2453 #[test]
2454 fn constraints_evaluate_to_zero_on_property_based_test_program_for_is_u32() {
2455 triton_constraints_evaluate_to_zero(property_based_test_program_for_is_u32())
2456 }
2457
2458 #[test]
2459 fn constraints_evaluate_to_zero_on_property_based_test_program_for_random_ram_access() {
2460 triton_constraints_evaluate_to_zero(property_based_test_program_for_random_ram_access())
2461 }
2462
2463 #[test]
2464 fn constraints_evaluate_to_zero_on_property_based_test_program_for_xx_dot_step() {
2465 triton_constraints_evaluate_to_zero(property_based_test_program_for_xx_dot_step());
2466 }
2467
2468 #[test]
2469 fn constraints_evaluate_to_zero_on_property_based_test_program_for_xb_dot_step() {
2470 triton_constraints_evaluate_to_zero(property_based_test_program_for_xb_dot_step());
2471 }
2472
2473 #[test]
2474 fn can_read_twice_from_same_ram_address_within_one_cycle() {
2475 for i in 0..x_field_element::EXTENSION_DEGREE {
2476 let program = triton_program! {
2479 dup 0
2480 addi {i}
2481 xx_dot_step
2482 halt
2483 };
2484 let program_and_input = ProgramAndInput::new(program);
2485 debug_assert!(program_and_input.clone().run().is_ok());
2486 triton_constraints_evaluate_to_zero(program_and_input);
2487 }
2488 }
2489
2490 #[test]
2491 fn claim_in_ram_corresponds_to_currently_running_program() {
2492 triton_constraints_evaluate_to_zero(
2493 test_program_claim_in_ram_corresponds_to_currently_running_program(),
2494 );
2495 }
2496
2497 macro_rules! check_constraints_fn {
2498 (fn $fn_name:ident for $table:ident) => {
2499 fn $fn_name(
2500 master_main_trace_table: ArrayView2<BFieldElement>,
2501 master_aux_trace_table: ArrayView2<XFieldElement>,
2502 challenges: &Challenges,
2503 ) {
2504 assert!(master_main_trace_table.nrows() == master_aux_trace_table.nrows());
2505 let challenges = &challenges.challenges;
2506
2507 let builder = ConstraintCircuitBuilder::new();
2508 for (constraint_idx, constraint) in $table::initial_constraints(&builder)
2509 .into_iter()
2510 .map(|constraint_monad| constraint_monad.consume())
2511 .enumerate()
2512 {
2513 let evaluated_constraint = constraint.evaluate(
2514 master_main_trace_table.slice(s![..1, ..]),
2515 master_aux_trace_table.slice(s![..1, ..]),
2516 challenges,
2517 );
2518 check!(
2519 xfe!(0) == evaluated_constraint,
2520 "{}: Initial constraint {constraint_idx} failed.",
2521 stringify!($table),
2522 );
2523 }
2524
2525 let builder = ConstraintCircuitBuilder::new();
2526 for (constraint_idx, constraint) in $table::consistency_constraints(&builder)
2527 .into_iter()
2528 .map(|constraint_monad| constraint_monad.consume())
2529 .enumerate()
2530 {
2531 for row_idx in 0..master_main_trace_table.nrows() {
2532 let evaluated_constraint = constraint.evaluate(
2533 master_main_trace_table.slice(s![row_idx..=row_idx, ..]),
2534 master_aux_trace_table.slice(s![row_idx..=row_idx, ..]),
2535 challenges,
2536 );
2537 check!(
2538 xfe!(0) == evaluated_constraint,
2539 "{}: Consistency constraint {constraint_idx} failed on row {row_idx}.",
2540 stringify!($table),
2541 );
2542 }
2543 }
2544
2545 let builder = ConstraintCircuitBuilder::new();
2546 for (constraint_idx, constraint) in $table::transition_constraints(&builder)
2547 .into_iter()
2548 .map(|constraint_monad| constraint_monad.consume())
2549 .enumerate()
2550 {
2551 for row_idx in 0..master_main_trace_table.nrows() - 1 {
2552 let evaluated_constraint = constraint.evaluate(
2553 master_main_trace_table.slice(s![row_idx..=row_idx + 1, ..]),
2554 master_aux_trace_table.slice(s![row_idx..=row_idx + 1, ..]),
2555 challenges,
2556 );
2557 check!(
2558 xfe!(0) == evaluated_constraint,
2559 "{}: Transition constraint {constraint_idx} failed on row {row_idx}.",
2560 stringify!($table),
2561 );
2562 }
2563 }
2564
2565 let builder = ConstraintCircuitBuilder::new();
2566 for (constraint_idx, constraint) in $table::terminal_constraints(&builder)
2567 .into_iter()
2568 .map(|constraint_monad| constraint_monad.consume())
2569 .enumerate()
2570 {
2571 let evaluated_constraint = constraint.evaluate(
2572 master_main_trace_table.slice(s![-1.., ..]),
2573 master_aux_trace_table.slice(s![-1.., ..]),
2574 challenges,
2575 );
2576 check!(
2577 xfe!(0) == evaluated_constraint,
2578 "{}: Terminal constraint {constraint_idx} failed.",
2579 stringify!($table),
2580 );
2581 }
2582 }
2583 };
2584 }
2585
2586 check_constraints_fn!(fn check_program_table_constraints for ProgramTable);
2587 check_constraints_fn!(fn check_processor_table_constraints for ProcessorTable);
2588 check_constraints_fn!(fn check_op_stack_table_constraints for OpStackTable);
2589 check_constraints_fn!(fn check_ram_table_constraints for RamTable);
2590 check_constraints_fn!(fn check_jump_stack_table_constraints for JumpStackTable);
2591 check_constraints_fn!(fn check_hash_table_constraints for HashTable);
2592 check_constraints_fn!(fn check_cascade_table_constraints for CascadeTable);
2593 check_constraints_fn!(fn check_lookup_table_constraints for LookupTable);
2594 check_constraints_fn!(fn check_u32_table_constraints for U32Table);
2595 check_constraints_fn!(fn check_cross_table_constraints for GrandCrossTableArg);
2596
2597 fn triton_constraints_evaluate_to_zero(program_and_input: ProgramAndInput) {
2598 let (_, _, master_main_table, master_aux_table, challenges) =
2599 master_tables_for_low_security_level(program_and_input);
2600
2601 let mbt = master_main_table.trace_table();
2602 let met = master_aux_table.trace_table();
2603 assert!(mbt.nrows() == met.nrows());
2604
2605 check_program_table_constraints(mbt, met, &challenges);
2606 check_processor_table_constraints(mbt, met, &challenges);
2607 check_op_stack_table_constraints(mbt, met, &challenges);
2608 check_ram_table_constraints(mbt, met, &challenges);
2609 check_jump_stack_table_constraints(mbt, met, &challenges);
2610 check_hash_table_constraints(mbt, met, &challenges);
2611 check_cascade_table_constraints(mbt, met, &challenges);
2612 check_lookup_table_constraints(mbt, met, &challenges);
2613 check_u32_table_constraints(mbt, met, &challenges);
2614 check_cross_table_constraints(mbt, met, &challenges);
2615 }
2616
2617 #[test]
2618 fn derived_constraints_evaluate_to_zero_on_halt() {
2619 derived_constraints_evaluate_to_zero(test_program_for_halt());
2620 }
2621
2622 fn derived_constraints_evaluate_to_zero(program_and_input: ProgramAndInput) {
2623 let (_, _, master_main_table, master_aux_table, challenges) =
2624 master_tables_for_low_security_level(program_and_input);
2625
2626 let master_main_trace_table = master_main_table.trace_table();
2627 let master_aux_trace_table = master_aux_table.trace_table();
2628
2629 let evaluated_initial_constraints = MasterAuxTable::evaluate_initial_constraints(
2630 master_main_trace_table.row(0),
2631 master_aux_trace_table.row(0),
2632 &challenges,
2633 );
2634 for (constraint_idx, evaluated_constraint) in
2635 evaluated_initial_constraints.into_iter().enumerate()
2636 {
2637 assert!(
2638 xfe!(0) == evaluated_constraint,
2639 "Initial constraint {constraint_idx} failed.",
2640 );
2641 }
2642
2643 for row_idx in 0..master_main_trace_table.nrows() {
2644 let evaluated_consistency_constraints =
2645 MasterAuxTable::evaluate_consistency_constraints(
2646 master_main_trace_table.row(row_idx),
2647 master_aux_trace_table.row(row_idx),
2648 &challenges,
2649 );
2650 for (constraint_idx, evaluated_constraint) in
2651 evaluated_consistency_constraints.into_iter().enumerate()
2652 {
2653 assert!(
2654 xfe!(0) == evaluated_constraint,
2655 "Consistency constraint {constraint_idx} failed in row {row_idx}.",
2656 );
2657 }
2658 }
2659
2660 for curr_row_idx in 0..master_main_trace_table.nrows() - 1 {
2661 let next_row_idx = curr_row_idx + 1;
2662 let evaluated_transition_constraints = MasterAuxTable::evaluate_transition_constraints(
2663 master_main_trace_table.row(curr_row_idx),
2664 master_aux_trace_table.row(curr_row_idx),
2665 master_main_trace_table.row(next_row_idx),
2666 master_aux_trace_table.row(next_row_idx),
2667 &challenges,
2668 );
2669 for (constraint_idx, evaluated_constraint) in
2670 evaluated_transition_constraints.into_iter().enumerate()
2671 {
2672 assert!(
2673 xfe!(0) == evaluated_constraint,
2674 "Transition constraint {constraint_idx} failed in row {curr_row_idx}.",
2675 );
2676 }
2677 }
2678
2679 let evaluated_terminal_constraints = MasterAuxTable::evaluate_terminal_constraints(
2680 master_main_trace_table.row(master_main_trace_table.nrows() - 1),
2681 master_aux_trace_table.row(master_aux_trace_table.nrows() - 1),
2682 &challenges,
2683 );
2684 for (constraint_idx, evaluated_constraint) in
2685 evaluated_terminal_constraints.into_iter().enumerate()
2686 {
2687 assert!(
2688 xfe!(0) == evaluated_constraint,
2689 "Terminal constraint {constraint_idx} failed.",
2690 );
2691 }
2692 }
2693
2694 #[test]
2695 fn prove_and_verify_simple_program() {
2696 prove_and_verify(
2697 test_program_hash_nop_nop_lt(),
2698 DEFAULT_LOG2_FRI_EXPANSION_FACTOR_FOR_TESTS,
2699 );
2700 }
2701
2702 #[test]
2703 fn prove_and_verify_halt_with_different_fri_expansion_factors() {
2704 for log_2_fri_expansion_factor in 1..5 {
2705 println!("Testing with log2_fri_expansion_factor = {log_2_fri_expansion_factor}");
2706 prove_and_verify(test_program_for_halt(), log_2_fri_expansion_factor);
2707 }
2708 }
2709
2710 #[test]
2711 fn prove_and_verify_fibonacci_100() {
2712 let program_and_input =
2713 ProgramAndInput::new(crate::example_programs::FIBONACCI_SEQUENCE.clone())
2714 .with_input(PublicInput::from(bfe_array![100]));
2715 prove_and_verify(
2716 program_and_input,
2717 DEFAULT_LOG2_FRI_EXPANSION_FACTOR_FOR_TESTS,
2718 );
2719 }
2720
2721 #[test]
2722 fn prove_verify_program_using_pick_and_place() {
2723 let input = bfe_vec![6, 3, 7, 5, 1, 2, 4, 4, 7, 3, 6, 1, 5, 2];
2724 let program = triton_program! { read_io 5 read_io 5 read_io 4 pick 2 pick 9 place 13 place 13 pick 0 pick 7 place 13 place 13 pick 2 pick 8 place 13 place 13 pick 3 pick 4 place 13 place 13 pick 0 pick 3 place 13 place 13 pick 0 pick 3 place 13 place 13 pick 1 pick 1 place 13 place 13 write_io 5 write_io 5 write_io 4 halt
2735 };
2736
2737 let program_and_input = ProgramAndInput::new(program).with_input(input);
2738 let output = program_and_input.clone().run().unwrap();
2739 let expected_output = bfe_vec![1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7];
2740 assert!(expected_output == output);
2741
2742 prove_and_verify(
2743 program_and_input,
2744 DEFAULT_LOG2_FRI_EXPANSION_FACTOR_FOR_TESTS,
2745 );
2746 }
2747
2748 #[test]
2749 fn constraints_evaluate_to_zero_on_many_u32_operations() {
2750 let many_u32_instructions = ProgramAndInput::new(
2751 crate::example_programs::PROGRAM_WITH_MANY_U32_INSTRUCTIONS.clone(),
2752 );
2753 triton_constraints_evaluate_to_zero(many_u32_instructions);
2754 }
2755
2756 #[test]
2757 fn prove_verify_many_u32_operations() {
2758 prove_and_verify(
2759 ProgramAndInput::new(
2760 crate::example_programs::PROGRAM_WITH_MANY_U32_INSTRUCTIONS.clone(),
2761 ),
2762 DEFAULT_LOG2_FRI_EXPANSION_FACTOR_FOR_TESTS,
2763 );
2764 }
2765
2766 #[proptest]
2767 fn verifying_arbitrary_proof_does_not_panic(
2768 #[strategy(arb())] stark: Stark,
2769 #[strategy(arb())] claim: Claim,
2770 #[strategy(arb())] proof: Proof,
2771 ) {
2772 let _verdict = stark.verify(&claim, &proof);
2773 }
2774
2775 #[proptest]
2776 fn negative_log_2_floor(
2777 #[strategy(arb())]
2778 #[filter(#st0.value() > u64::from(u32::MAX))]
2779 st0: BFieldElement,
2780 ) {
2781 let program = triton_program!(push {st0} log_2_floor halt);
2782 let_assert!(Err(err) = VM::run(program, [].into(), [].into()));
2783 let_assert!(InstructionError::OpStackError(err) = err.source);
2784 let_assert!(OpStackError::FailedU32Conversion(element) = err);
2785 assert!(st0 == element);
2786 }
2787
2788 #[test]
2789 fn negative_log_2_floor_of_0() {
2790 let program = triton_program!(push 0 log_2_floor halt);
2791 let_assert!(Err(err) = VM::run(program, [].into(), [].into()));
2792 let_assert!(InstructionError::LogarithmOfZero = err.source);
2793 }
2794
2795 #[test]
2796 fn deep_update() {
2797 let domain_length = 1 << 10;
2798 let domain = ArithmeticDomain::of_length(domain_length).unwrap();
2799
2800 let poly_degree = rand::rng().random_range(2..20);
2801 let low_deg_poly_coeffs: Vec<XFieldElement> = random_elements(poly_degree);
2802 let low_deg_poly = Polynomial::new(low_deg_poly_coeffs.clone());
2803 let low_deg_codeword = domain.evaluate(&low_deg_poly);
2804
2805 let out_of_domain_point: XFieldElement = rand::rng().random();
2806 let out_of_domain_value = low_deg_poly.evaluate(out_of_domain_point);
2807
2808 let deep_poly = Prover::deep_codeword(
2809 &low_deg_codeword,
2810 domain,
2811 out_of_domain_point,
2812 out_of_domain_value,
2813 );
2814 let poly_of_maybe_low_degree = domain.interpolate(&deep_poly);
2815 assert!(poly_degree as isize - 2 == poly_of_maybe_low_degree.degree());
2816
2817 let bogus_out_of_domain_value = rand::rng().random();
2818 let bogus_deep_poly = Prover::deep_codeword(
2819 &low_deg_codeword,
2820 domain,
2821 out_of_domain_point,
2822 bogus_out_of_domain_value,
2823 );
2824 let poly_of_hopefully_high_degree = domain.interpolate(&bogus_deep_poly);
2825 assert!(domain_length as isize - 1 == poly_of_hopefully_high_degree.degree());
2826 }
2827
2828 fn assert_polynomial_equals_recomposed_segments<const N: usize, FF: FiniteField>(
2831 f: &Polynomial<FF>,
2832 segments: &[Polynomial<FF>; N],
2833 x: FF,
2834 ) {
2835 let x_pow_n = x.mod_pow_u32(N as u32);
2836 let evaluate_segment = |(segment_idx, segment): (_, &Polynomial<_>)| {
2837 segment.evaluate::<_, FF>(x_pow_n) * x.mod_pow_u32(segment_idx as u32)
2838 };
2839 let evaluated_segments = segments.iter().enumerate().map(evaluate_segment);
2840 let sum_of_evaluated_segments = evaluated_segments.fold(FF::zero(), |acc, x| acc + x);
2841 assert!(f.evaluate::<_, FF>(x) == sum_of_evaluated_segments);
2842 }
2843
2844 fn assert_segments_degrees_are_small_enough<const N: usize, FF: FiniteField>(
2845 f: &Polynomial<FF>,
2846 segments: &[Polynomial<FF>; N],
2847 ) {
2848 let max_allowed_degree = f.degree() / (N as isize);
2849 let all_degrees_are_small_enough =
2850 segments.iter().all(|s| s.degree() <= max_allowed_degree);
2851 assert!(all_degrees_are_small_enough);
2852 }
2853
2854 #[test]
2855 fn split_polynomial_into_segments_of_unequal_size() {
2856 let coefficients: [XFieldElement; 211] = rand::rng().random();
2857 let f = Polynomial::new(coefficients.to_vec());
2858
2859 let segments_2 = Prover::split_polynomial_into_segments::<2, _>(f.clone());
2860 let segments_3 = Prover::split_polynomial_into_segments::<3, _>(f.clone());
2861 let segments_4 = Prover::split_polynomial_into_segments::<4, _>(f.clone());
2862 let segments_7 = Prover::split_polynomial_into_segments::<7, _>(f.clone());
2863
2864 assert_segments_degrees_are_small_enough(&f, &segments_2);
2865 assert_segments_degrees_are_small_enough(&f, &segments_3);
2866 assert_segments_degrees_are_small_enough(&f, &segments_4);
2867 assert_segments_degrees_are_small_enough(&f, &segments_7);
2868
2869 let x = rand::rng().random();
2870 assert_polynomial_equals_recomposed_segments(&f, &segments_2, x);
2871 assert_polynomial_equals_recomposed_segments(&f, &segments_3, x);
2872 assert_polynomial_equals_recomposed_segments(&f, &segments_4, x);
2873 assert_polynomial_equals_recomposed_segments(&f, &segments_7, x);
2874 }
2875
2876 #[test]
2877 fn split_polynomial_into_segments_of_equal_size() {
2878 let coefficients: [BFieldElement; 2 * 3 * 4 * 7] = rand::rng().random();
2879 let f = Polynomial::new(coefficients.to_vec());
2880
2881 let segments_2 = Prover::split_polynomial_into_segments::<2, _>(f.clone());
2882 let segments_3 = Prover::split_polynomial_into_segments::<3, _>(f.clone());
2883 let segments_4 = Prover::split_polynomial_into_segments::<4, _>(f.clone());
2884 let segments_7 = Prover::split_polynomial_into_segments::<7, _>(f.clone());
2885
2886 assert_segments_degrees_are_small_enough(&f, &segments_2);
2887 assert_segments_degrees_are_small_enough(&f, &segments_3);
2888 assert_segments_degrees_are_small_enough(&f, &segments_4);
2889 assert_segments_degrees_are_small_enough(&f, &segments_7);
2890
2891 let x = rand::rng().random();
2892 assert_polynomial_equals_recomposed_segments(&f, &segments_2, x);
2893 assert_polynomial_equals_recomposed_segments(&f, &segments_3, x);
2894 assert_polynomial_equals_recomposed_segments(&f, &segments_4, x);
2895 assert_polynomial_equals_recomposed_segments(&f, &segments_7, x);
2896 }
2897
2898 #[derive(Debug, Clone, Eq, PartialEq, Hash, test_strategy::Arbitrary)]
2899 struct SegmentifyProptestData {
2900 #[strategy(2_usize..8)]
2901 log_trace_length: usize,
2902
2903 #[strategy(1_usize..=#log_trace_length.min(4))]
2904 log_num_cosets: usize,
2905
2906 #[strategy(1_usize..6)]
2907 log_expansion_factor: usize,
2908
2909 #[strategy(vec(arb(), (1 << #log_num_cosets) * (1 << #log_trace_length)))]
2910 #[map(Polynomial::new)]
2911 polynomial: Polynomial<'static, XFieldElement>,
2912
2913 #[strategy(arb())]
2914 psi: BFieldElement,
2915
2916 #[strategy(arb())]
2917 random_point: XFieldElement,
2918 }
2919
2920 #[proptest]
2921 fn polynomial_segments_cohere_with_originating_polynomial(test_data: SegmentifyProptestData) {
2922 fn segmentify_and_assert_coherence<const N: usize>(
2923 test_data: &SegmentifyProptestData,
2924 ) -> TestCaseResult {
2925 let num_cosets = 1 << test_data.log_num_cosets;
2926 let trace_length = 1 << test_data.log_trace_length;
2927 let expansion_factor = 1 << test_data.log_expansion_factor;
2928
2929 let iota =
2930 BFieldElement::primitive_root_of_unity((trace_length * num_cosets) as u64).unwrap();
2931 let trace_domain = ArithmeticDomain::of_length(trace_length)?;
2932 let fri_domain = ArithmeticDomain::of_length(trace_length * expansion_factor)?
2933 .with_offset(test_data.psi);
2934
2935 let coset_evaluations = (0..u32::try_from(num_cosets)?)
2936 .flat_map(|i| {
2937 let coset = trace_domain.with_offset(iota.mod_pow_u32(i) * test_data.psi);
2938 coset.evaluate(&test_data.polynomial)
2939 })
2940 .collect();
2941 let coset_evaluations =
2942 Array2::from_shape_vec((trace_length, num_cosets).f(), coset_evaluations)?;
2943
2944 let (actual_segment_codewords, segment_polynomials) =
2945 Prover::segmentify::<N>(coset_evaluations, test_data.psi, iota, fri_domain);
2946
2947 prop_assert_eq!(N, actual_segment_codewords.ncols());
2948 prop_assert_eq!(N, segment_polynomials.len());
2949
2950 let segments_evaluated = (0..)
2951 .zip(&segment_polynomials)
2952 .map(|(segment_index, segment_polynomial)| -> XFieldElement {
2953 let point_to_the_seg_idx = test_data.random_point.mod_pow_u32(segment_index);
2954 let point_to_the_num_seg = test_data.random_point.mod_pow_u32(N as u32);
2955 let segment_in_point_to_the_num_seg =
2956 segment_polynomial.evaluate_in_same_field(point_to_the_num_seg);
2957 point_to_the_seg_idx * segment_in_point_to_the_num_seg
2958 })
2959 .sum::<XFieldElement>();
2960 let evaluation_in_random_point = test_data
2961 .polynomial
2962 .evaluate_in_same_field(test_data.random_point);
2963 prop_assert_eq!(segments_evaluated, evaluation_in_random_point);
2964
2965 let segments_codewords = segment_polynomials
2966 .iter()
2967 .flat_map(|polynomial| Array1::from(fri_domain.evaluate(polynomial)))
2968 .collect_vec();
2969 let segments_codewords =
2970 Array2::from_shape_vec((fri_domain.length, N).f(), segments_codewords)?;
2971 prop_assert_eq!(segments_codewords, actual_segment_codewords);
2972
2973 Ok(())
2974 }
2975
2976 let num_cosets = 1 << test_data.log_num_cosets;
2977 if num_cosets >= 1 {
2978 segmentify_and_assert_coherence::<1>(&test_data)?;
2979 }
2980 if num_cosets >= 2 {
2981 segmentify_and_assert_coherence::<2>(&test_data)?;
2982 }
2983 if num_cosets >= 4 {
2984 segmentify_and_assert_coherence::<4>(&test_data)?;
2985 }
2986 if num_cosets >= 8 {
2987 segmentify_and_assert_coherence::<8>(&test_data)?;
2988 }
2989 if num_cosets >= 16 {
2990 segmentify_and_assert_coherence::<16>(&test_data)?;
2991 }
2992 }
2993
2994 #[proptest]
2995 fn linear_combination_weights_samples_correct_number_of_elements(
2996 #[strategy(arb())] mut proof_stream: ProofStream,
2997 ) {
2998 let weights = LinearCombinationWeights::sample(&mut proof_stream);
2999
3000 prop_assert_eq!(MasterMainTable::NUM_COLUMNS, weights.main.len());
3001 prop_assert_eq!(MasterAuxTable::NUM_COLUMNS, weights.aux.len());
3002 prop_assert_eq!(NUM_QUOTIENT_SEGMENTS, weights.quot_segments.len());
3003 prop_assert_eq!(NUM_DEEP_CODEWORD_COMPONENTS, weights.deep.len());
3004 prop_assert_eq!(
3005 MasterMainTable::NUM_COLUMNS + MasterAuxTable::NUM_COLUMNS,
3006 weights.main_and_aux().len()
3007 );
3008 }
3009
3010 pub fn program_executing_every_instruction() -> ProgramAndInput {
3012 let m_step_mem_addr = 100_000;
3013
3014 let program = triton_program! {
3015 push {m_step_mem_addr} push 0 push 5 read_io 5 merkle_step merkle_step_mem divine 5 assert_vector pop 5 assert pop 2 push 1 push 2 push 3 place 2 pick 1 swap 2 dup 2 assert addi -2 assert addi -1 assert assert push 0 push 0 push 0 push 500 push 800 xb_dot_step xx_dot_step write_io 5 push 1 push 2 push 3 push 7 push 8 push 9 dup 3 dup 3 dup 3 xx_add dup 4 dup 4 dup 4 xx_mul x_invert push 42 xb_mul add mul addi 0 invert mul add eq pop 1 push 38 push 2 pow push 1337 add split dup 1 dup 1 lt pop 1 dup 1 and dup 1 xor push 9 log_2_floor pop 1 div_mod pop_count pop 2 sponge_init divine 5 divine 5 sponge_absorb push 42 sponge_absorb_mem pop 1 sponge_squeeze hash pop 5 push 300 read_mem 5 swap 6 write_mem 5 pop 1 push 0 skiz nop push 1 skiz nop push 0 push 2 push 0 push 0 push 0 push 0 push 0 call rec_or_ret pop 5 pop 2 push 2 call rec pop 1
3115 halt
3116
3117 rec:
3120 dup 0 push 0 eq skiz return push -1 add recurse rec_or_ret:
3128 swap 5 push -1 add swap 5 recurse_or_return };
3133
3134 let tree_node_5 = Digest::new(bfe_array![5; 5]);
3135 let tree_node_4 = Digest::new(bfe_array![4; 5]);
3136 let tree_node_3 = Digest::new(bfe_array![3; 5]);
3137 let tree_node_2 = Tip5::hash_pair(tree_node_4, tree_node_5);
3138 let tree_node_1 = Tip5::hash_pair(tree_node_2, tree_node_3);
3139
3140 let public_input = tree_node_5.values().to_vec();
3141
3142 let secret_input = [tree_node_1.reversed().values().to_vec(), bfe_vec![1337; 10]].concat();
3143 let mut ram = (0..)
3144 .zip(42..)
3145 .take(1_000)
3146 .map(|(l, r)| (bfe!(l), bfe!(r)))
3147 .collect::<HashMap<_, _>>();
3148 for (address, digest_element) in (m_step_mem_addr..).zip(tree_node_3.values()) {
3149 ram.insert(bfe!(address), digest_element);
3150 }
3151 let non_determinism = NonDeterminism::new(secret_input)
3152 .with_digests([tree_node_4])
3153 .with_ram(ram);
3154
3155 ProgramAndInput::new(program)
3156 .with_input(public_input)
3157 .with_non_determinism(non_determinism)
3158 }
3159
3160 #[test]
3161 fn program_executing_every_instruction_actually_executes_every_instruction() {
3162 let ProgramAndInput {
3163 program,
3164 public_input,
3165 non_determinism,
3166 } = program_executing_every_instruction();
3167 let (aet, _) = VM::trace_execution(program, public_input, non_determinism).unwrap();
3168 let opcodes_of_all_executed_instructions = aet
3169 .processor_trace
3170 .column(ProcessorMainColumn::CI.main_index())
3171 .iter()
3172 .copied()
3173 .collect::<HashSet<_>>();
3174
3175 let all_opcodes = Instruction::iter()
3176 .map(|instruction| instruction.opcode_b())
3177 .collect::<HashSet<_>>();
3178
3179 all_opcodes
3180 .difference(&opcodes_of_all_executed_instructions)
3181 .for_each(|&opcode| {
3182 let instruction = Instruction::try_from(opcode).unwrap();
3183 eprintln!("Instruction {instruction} was not executed.");
3184 });
3185 assert_eq!(all_opcodes, opcodes_of_all_executed_instructions);
3186 }
3187
3188 #[test]
3189 fn constraints_evaluate_to_zero_on_program_executing_every_instruction() {
3190 triton_constraints_evaluate_to_zero(program_executing_every_instruction());
3191 }
3192}