Skip to main content

tasm_lib/verifier/
stark_verify.rs

1use itertools::Itertools;
2use triton_vm::challenges::Challenges;
3use triton_vm::prelude::*;
4use triton_vm::proof_item::ProofItemVariant;
5use triton_vm::proof_stream::ProofStream;
6use triton_vm::table::NUM_QUOTIENT_SEGMENTS;
7use triton_vm::table::NUM_RANDOMIZED_QUOTIENT_SEGMENTS;
8use triton_vm::table::master_table::MasterAuxTable;
9use triton_vm::table::master_table::MasterMainTable;
10use twenty_first::math::x_field_element::EXTENSION_DEGREE;
11use twenty_first::prelude::MerkleTreeInclusionProof;
12
13use super::master_table::air_constraint_evaluation::AirConstraintEvaluation;
14use super::master_table::air_constraint_evaluation::MemoryLayout;
15use crate::arithmetic::bfe::primitive_root_of_unity::PrimitiveRootOfUnity;
16use crate::array::horner_evaluation::HornerEvaluation;
17use crate::array::inner_product_of_three_rows_with_weights::InnerProductOfThreeRowsWithWeights;
18use crate::array::inner_product_of_three_rows_with_weights::MainElementType;
19use crate::array::inner_product_of_xfes::InnerProductOfXfes;
20use crate::field;
21use crate::hashing::algebraic_hasher::sample_scalar_one::SampleScalarOne;
22use crate::hashing::algebraic_hasher::sample_scalars_static_length_dyn_malloc::SampleScalarsStaticLengthDynMalloc;
23use crate::prelude::*;
24use crate::verifier::challenges;
25use crate::verifier::claim::instantiate_fiat_shamir_with_claim::InstantiateFiatShamirWithClaim;
26use crate::verifier::claim::shared::claim_type;
27use crate::verifier::fri;
28use crate::verifier::fri::verify::FriSnippet;
29use crate::verifier::fri::verify::FriVerify;
30use crate::verifier::master_table::divide_out_zerofiers::DivideOutZerofiers;
31use crate::verifier::master_table::verify_table_rows::ColumnType;
32use crate::verifier::master_table::verify_table_rows::VerifyTableRows;
33use crate::verifier::out_of_domain_points::OodPoint;
34use crate::verifier::out_of_domain_points::OutOfDomainPoints;
35use crate::verifier::vm_proof_iter::dequeue_next_as::DequeueNextAs;
36use crate::verifier::vm_proof_iter::drop::Drop;
37use crate::verifier::vm_proof_iter::new::New;
38
39pub(crate) const NUM_PROOF_ITEMS_PER_FRI_ROUND: usize = 2;
40pub(crate) const NUM_PROOF_ITEMS_EXCLUDING_FRI: usize = 16;
41
42/// Verify a STARK proof.
43///
44/// Verify a STARK proof located in memory. Assumes the nondeterministic digests
45/// stream has been updated with the digests extracted from the proof using
46/// [`update_nondeterminism`](Self::update_nondeterminism). Crashes the VM if the
47/// proof is invalid.
48///
49/// Stack signature:
50///  - BEFORE: _ *claim *proof
51///  - AFTER:  _
52#[derive(Debug, Copy, Clone)]
53pub struct StarkVerify {
54    stark: Stark,
55    memory_layout: MemoryLayout,
56}
57
58impl StarkVerify {
59    const LOG2_PADDED_HEIGHT_TOO_LARGE: i128 = 238;
60    const LOG2_PADDED_HEIGHT_TOO_SMALL: i128 = 239;
61
62    pub fn new_with_static_layout(stark: Stark) -> Self {
63        Self {
64            stark,
65            memory_layout: MemoryLayout::conventional_static(),
66        }
67    }
68
69    pub fn new_with_dynamic_layout(stark: Stark) -> Self {
70        Self {
71            stark,
72            memory_layout: MemoryLayout::conventional_dynamic(),
73        }
74    }
75
76    /// The number of nondeterministic digests that will be
77    /// consumed when this snippet verifies the given proof.
78    pub fn number_of_nondeterministic_digests_consumed(&self, proof: &Proof) -> usize {
79        const NUM_FULL_DOMAIN_AUTH_PATHS: usize = 4;
80
81        let padded_height = proof.padded_height().unwrap();
82        let fri_params = self.stark.fri(padded_height).unwrap();
83        let num_fri_rounds = fri_params.num_rounds();
84
85        let mut j = 0;
86        let mut tree_height: usize = fri_params.domain.len().ilog2().try_into().unwrap();
87
88        let mut acc = NUM_FULL_DOMAIN_AUTH_PATHS * tree_height * fri_params.num_collinearity_checks;
89        while j < num_fri_rounds {
90            acc += fri_params.num_collinearity_checks * tree_height;
91            j += 1;
92            tree_height -= 1;
93        }
94
95        acc
96    }
97
98    /// The number of nondeterministic individual tokens that will be consumed when
99    /// this snippet verifies the given (claim, proof) pair.
100    // Right now this number is zero, but that might change in the future.
101    pub fn number_of_nondeterministic_tokens_consumed(
102        &self,
103        _proof: &Proof,
104        _claim: &Claim,
105    ) -> usize {
106        0
107    }
108
109    /// Prepares the non-determinism for verifying a STARK proof. Specifically,
110    /// extracts the digests for traversing authentication paths and appends them
111    /// to nondeterministic digests. Leaves memory and individual tokens intact.
112    pub fn update_nondeterminism(
113        &self,
114        nondeterminism: &mut NonDeterminism,
115        proof: &Proof,
116        claim: &Claim,
117    ) {
118        nondeterminism
119            .digests
120            .append(&mut self.extract_nondeterministic_digests(proof, claim));
121    }
122
123    fn extract_nondeterministic_digests(&self, proof: &Proof, claim: &Claim) -> Vec<Digest> {
124        const NUM_DEEP_CODEWORD_COMPONENTS: usize = 4;
125
126        fn extract_paths<R: BFieldCodec>(
127            indices: Vec<usize>,
128            leaf_preimages: Vec<R>,
129            tree_height: u32,
130            authentication_structure: Vec<Digest>,
131        ) -> Vec<Vec<Digest>> {
132            let indexed_leafs = indices
133                .into_iter()
134                .zip(leaf_preimages.iter().map(Tip5::hash))
135                .collect();
136            MerkleTreeInclusionProof {
137                tree_height,
138                indexed_leafs,
139                authentication_structure,
140            }
141            .into_authentication_paths()
142            .unwrap()
143        }
144
145        // We do need to carefully update the sponge state because otherwise
146        // we end up sampling indices that generate different authentication
147        // paths.
148        let mut proof_stream = ProofStream::try_from(proof).unwrap();
149        proof_stream.alter_fiat_shamir_state_with(claim);
150        let log2_padded_height = proof_stream
151            .dequeue()
152            .unwrap()
153            .try_into_log2_padded_height()
154            .unwrap();
155
156        // Main-table Merkle root
157        let _main_table_root = proof_stream
158            .dequeue()
159            .unwrap()
160            .try_into_merkle_root()
161            .unwrap();
162
163        // Auxiliary challenge weights
164        let _challenges = proof_stream.sample_scalars(Challenges::SAMPLE_COUNT);
165
166        // Auxiliary-table Merkle root
167        let _aux_mt_root = proof_stream
168            .dequeue()
169            .unwrap()
170            .try_into_merkle_root()
171            .unwrap();
172
173        // Quotient codeword weights
174        proof_stream.sample_scalars(MasterAuxTable::NUM_CONSTRAINTS);
175
176        // Quotient codeword Merkle root
177        let _quotient_root = proof_stream
178            .dequeue()
179            .unwrap()
180            .try_into_merkle_root()
181            .unwrap();
182
183        // Out-of-domain point current row
184        let _out_of_domain_point_curr_row = proof_stream.sample_scalars(1);
185
186        // Six out-of-domain values
187        proof_stream
188            .dequeue()
189            .unwrap()
190            .try_into_out_of_domain_main_row()
191            .unwrap();
192        proof_stream
193            .dequeue()
194            .unwrap()
195            .try_into_out_of_domain_aux_row()
196            .unwrap();
197        proof_stream
198            .dequeue()
199            .unwrap()
200            .try_into_out_of_domain_main_row()
201            .unwrap();
202        proof_stream
203            .dequeue()
204            .unwrap()
205            .try_into_out_of_domain_aux_row()
206            .unwrap();
207        proof_stream
208            .dequeue()
209            .unwrap()
210            .try_into_out_of_domain_quot_segments()
211            .unwrap();
212        proof_stream
213            .dequeue()
214            .unwrap()
215            .try_into_out_of_domain_quot_segments()
216            .unwrap();
217
218        // `beqd_weights`
219        proof_stream.sample_scalars(
220            MasterMainTable::NUM_COLUMNS
221                + MasterAuxTable::NUM_COLUMNS
222                + NUM_RANDOMIZED_QUOTIENT_SEGMENTS
223                + NUM_DEEP_CODEWORD_COMPONENTS,
224        );
225
226        // FRI digests
227        let padded_height = 1 << log2_padded_height;
228        let fri = self.stark.fri(padded_height).unwrap();
229        let fri_proof_stream = proof_stream.clone();
230        let fri_verify_result = fri.verify(&mut proof_stream).unwrap();
231        let indices = fri_verify_result.iter().map(|(i, _)| *i).collect_vec();
232        let tree_height = fri.domain.len().ilog2();
233        let fri_digests =
234            FriVerify::from(fri).extract_digests_required_for_proving(&fri_proof_stream);
235
236        // main
237        let main_table_rows = proof_stream
238            .dequeue()
239            .unwrap()
240            .try_into_master_main_table_rows()
241            .unwrap();
242        let main_authentication_structure = proof_stream
243            .dequeue()
244            .unwrap()
245            .try_into_authentication_structure()
246            .unwrap();
247        let main_tree_auth_paths = extract_paths(
248            indices.clone(),
249            main_table_rows,
250            tree_height,
251            main_authentication_structure,
252        );
253
254        // aux
255        let aux_table_rows = proof_stream
256            .dequeue()
257            .unwrap()
258            .try_into_master_aux_table_rows()
259            .unwrap();
260        let aux_authentication_structure = proof_stream
261            .dequeue()
262            .unwrap()
263            .try_into_authentication_structure()
264            .unwrap();
265        let aux_tree_auth_paths = extract_paths(
266            indices.clone(),
267            aux_table_rows,
268            tree_height,
269            aux_authentication_structure,
270        );
271
272        // quotient
273        let quot_table_rows = proof_stream
274            .dequeue()
275            .unwrap()
276            .try_into_quot_segments_elements()
277            .unwrap();
278        let quot_authentication_structure = proof_stream
279            .dequeue()
280            .unwrap()
281            .try_into_authentication_structure()
282            .unwrap();
283        let quot_tree_auth_paths = extract_paths(
284            indices,
285            quot_table_rows,
286            tree_height,
287            quot_authentication_structure,
288        );
289
290        let stark_digests = [
291            main_tree_auth_paths,
292            aux_tree_auth_paths,
293            quot_tree_auth_paths,
294        ]
295        .concat()
296        .concat();
297
298        [fri_digests, stark_digests].concat()
299    }
300}
301
302impl BasicSnippet for StarkVerify {
303    fn parameters(&self) -> Vec<(DataType, String)> {
304        let claim_type = DataType::StructRef(claim_type());
305        vec![
306            (claim_type, "claim".to_string()),
307            (DataType::VoidPointer, "*proof".to_string()),
308        ]
309    }
310
311    fn return_values(&self) -> Vec<(DataType, String)> {
312        vec![]
313    }
314
315    fn entrypoint(&self) -> String {
316        let memory_layout_category = self.memory_layout.label_friendly_name();
317        format!("tasmlib_verifier_stark_verify_{memory_layout_category}")
318    }
319
320    fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
321        const NUM_DEEP_CODEWORD_COMPONENTS: usize = 4;
322        const NUM_OOD_ROWS_WO_QUOTIENT: u32 = 4;
323
324        fn fri_snippet() -> FriSnippet {
325            FriSnippet {
326                #[cfg(test)]
327                test_instance: FriVerify::dummy(),
328            }
329        }
330
331        let entrypoint = self.entrypoint();
332
333        let proof_to_vm_proof_iter = library.import(Box::new(New));
334        let drop_vm_proof_iter = library.import(Box::new(Drop));
335
336        let ood_curr_row_main_and_aux_value_pointer_alloc =
337            library.kmalloc(EXTENSION_DEGREE.try_into().unwrap());
338        let ood_next_row_main_and_aux_value_pointer_alloc =
339            library.kmalloc(EXTENSION_DEGREE.try_into().unwrap());
340        let ood_curr_row_quotient_segment_value_for_p_pointer_alloc =
341            library.kmalloc(EXTENSION_DEGREE.try_into().unwrap());
342        let ood_curr_row_quotient_segment_value_for_r_pointer_alloc =
343            library.kmalloc(EXTENSION_DEGREE.try_into().unwrap());
344
345        let ood_curr_row_quot_segments_for_p_pointer_alloc = library.kmalloc(1);
346        let ood_curr_row_quot_segments_for_r_pointer_alloc = library.kmalloc(1);
347
348        let instantiate_fiat_shamir_with_claim =
349            library.import(Box::new(InstantiateFiatShamirWithClaim));
350        let next_as_log_2_padded_height = library.import(Box::new(DequeueNextAs {
351            proof_item: ProofItemVariant::Log2PaddedHeight,
352        }));
353        let next_as_merkleroot = library.import(Box::new(DequeueNextAs {
354            proof_item: ProofItemVariant::MerkleRoot,
355        }));
356        let next_as_outofdomainmainrow = library.import(Box::new(DequeueNextAs {
357            proof_item: ProofItemVariant::OutOfDomainMainRow,
358        }));
359        let next_as_outofdomainauxrow = library.import(Box::new(DequeueNextAs {
360            proof_item: ProofItemVariant::OutOfDomainAuxRow,
361        }));
362        let next_as_outofdomainquotientsegments = library.import(Box::new(DequeueNextAs {
363            proof_item: ProofItemVariant::OutOfDomainQuotientSegments,
364        }));
365        let next_as_maintablerows = library.import(Box::new(DequeueNextAs {
366            proof_item: ProofItemVariant::MasterMainTableRows,
367        }));
368        let next_as_authentication_path = library.import(Box::new(DequeueNextAs {
369            proof_item: ProofItemVariant::AuthenticationStructure,
370        }));
371        let next_as_auxtablerows = library.import(Box::new(DequeueNextAs {
372            proof_item: ProofItemVariant::MasterAuxTableRows,
373        }));
374        let next_as_quotient_segment_elements = library.import(Box::new(DequeueNextAs {
375            proof_item: ProofItemVariant::QuotientSegmentsElements,
376        }));
377        let derive_fri_parameters =
378            library.import(Box::new(fri::derive_from_stark::DeriveFriFromStark {
379                stark: self.stark,
380            }));
381        let num_collinearity_checks_field = field!(FriVerify::num_collinearity_checks);
382        let domain_length_field = field!(FriVerify::domain_length);
383        let domain_offset_field = field!(FriVerify::domain_offset);
384        let domain_generator_field = field!(FriVerify::domain_generator);
385
386        let fri_verify = library.import(Box::new(fri_snippet()));
387
388        let get_challenges = library.import(Box::new(
389            challenges::new_generic_dyn_claim::NewGenericDynClaim::tvm_challenges(
390                self.memory_layout.challenges_pointer(),
391            ),
392        ));
393        let sample_quotient_codeword_weights =
394            library.import(Box::new(SampleScalarsStaticLengthDynMalloc {
395                num_elements: MasterAuxTable::NUM_CONSTRAINTS,
396            }));
397        let domain_generator = library.import(Box::new(PrimitiveRootOfUnity));
398        let sample_scalar_one = library.import(Box::new(SampleScalarOne));
399        let calculate_out_of_domain_points = library.import(Box::new(OutOfDomainPoints));
400        let divide_out_zerofiers = library.import(Box::new(DivideOutZerofiers));
401        let inner_product_quotient_summands = library.import(Box::new(InnerProductOfXfes {
402            length: MasterAuxTable::NUM_CONSTRAINTS,
403        }));
404        let horner_evaluation_of_ood_curr_row_quot_segments =
405            library.import(Box::new(HornerEvaluation {
406                num_coefficients: NUM_QUOTIENT_SEGMENTS,
407            }));
408        let sample_beqd_weights = library.import(Box::new(SampleScalarsStaticLengthDynMalloc {
409            num_elements: MasterMainTable::NUM_COLUMNS
410                + MasterAuxTable::NUM_COLUMNS
411                + NUM_RANDOMIZED_QUOTIENT_SEGMENTS
412                + NUM_DEEP_CODEWORD_COMPONENTS,
413        }));
414        let verify_main_table_rows = library.import(Box::new(VerifyTableRows {
415            column_type: ColumnType::Main,
416        }));
417        let verify_aux_table_rows = library.import(Box::new(VerifyTableRows {
418            column_type: ColumnType::Aux,
419        }));
420        let verify_quotient_segments = library.import(Box::new(VerifyTableRows {
421            column_type: ColumnType::Quotient,
422        }));
423        let inner_product_three_rows_with_weights_bfe_main = library.import(Box::new(
424            InnerProductOfThreeRowsWithWeights::triton_vm_parameters(MainElementType::Bfe),
425        ));
426        let inner_product_three_rows_with_weights_xfe_main = library.import(Box::new(
427            InnerProductOfThreeRowsWithWeights::triton_vm_parameters(MainElementType::Xfe),
428        ));
429        let inner_product_4_xfes = library.import(Box::new(InnerProductOfXfes { length: 4 }));
430        let quotient_segment_codeword_weights_from_be_weights = triton_asm!(
431            // _ *beqd_ws
432
433            addi {(MasterMainTable::NUM_COLUMNS + MasterAuxTable::NUM_COLUMNS) * EXTENSION_DEGREE}
434            // _ *quotient_segment_weights
435        );
436        let deep_codeword_weights_read_address = |n: usize| {
437            assert!(n < NUM_DEEP_CODEWORD_COMPONENTS);
438            triton_asm!(
439                // _ *beqd_ws
440
441                addi {(MasterMainTable::NUM_COLUMNS + MasterAuxTable::NUM_COLUMNS + NUM_RANDOMIZED_QUOTIENT_SEGMENTS + n) * EXTENSION_DEGREE + {EXTENSION_DEGREE - 1}}
442                // _ *deep_codeword_weight[n]_last_word
443            )
444        };
445
446        let dequeue_four_ood_rows = triton_asm! {
447            // _ *proof_iter
448            dup 0
449            call {next_as_outofdomainmainrow}
450            hint out_of_domain_curr_main_row: Pointer = stack[0]
451
452            dup 1
453            call {next_as_outofdomainauxrow}
454            hint out_of_domain_curr_aux_row: Pointer = stack[0]
455
456            dup 2
457            call {next_as_outofdomainmainrow}
458            hint out_of_domain_next_main_row: Pointer = stack[0]
459
460            dup 3
461            call {next_as_outofdomainauxrow}
462            hint out_of_domain_next_aux_row: Pointer = stack[0]
463            // _ *proof_iter *curr_main *curr_aux *next_main *next_aux
464        };
465
466        // BEFORE:
467        // _ *p_iter - - - *quot_cw_ws - dom_gen [out_of_domain_curr_row] trace_domain_len *proof_iter *curr_main *curr_aux *next_main *next_aux
468        // AFTER:
469        // _ *p_iter - - - *quot_cw_ws - dom_gen [out_of_domain_curr_row] trace_domain_len *air_evaluation_result
470        let ood_pointers_alloc = library.kmalloc(NUM_OOD_ROWS_WO_QUOTIENT);
471        let evaluate_air_and_store_ood_pointers = match self.memory_layout {
472            MemoryLayout::Static(static_layout) => {
473                let static_eval =
474                    library.import(Box::new(AirConstraintEvaluation::new_static(static_layout)));
475                triton_asm! {
476                    push {ood_pointers_alloc.write_address()}
477                    write_mem {ood_pointers_alloc.num_words()}
478
479                    pop 2
480                    // _ ... trace_domain_len
481
482                    call {static_eval}
483                    // _ ... trace_domain_len *air_evaluation_result
484                }
485            }
486            MemoryLayout::Dynamic(dynamic_layout) => {
487                let dynamic_eval = library.import(Box::new(AirConstraintEvaluation::new_dynamic(
488                    dynamic_layout,
489                )));
490                triton_asm! {
491                    // store pointers to static memory
492                    dup 3
493                    dup 3
494                    dup 3
495                    dup 3
496                    push {ood_pointers_alloc.write_address()}
497                    write_mem {ood_pointers_alloc.num_words()}
498                    pop 1
499                    // _ ... trace_domain_len *proof_iter *curr_main *curr_aux *next_main *next_aux
500
501                    call {dynamic_eval}
502                    // _ ... trace_domain_len *proof_iter *air_evaluation_result
503
504                    pick 1 pop 1
505                    // _ ... trace_domain_len *air_evaluation_result
506                }
507            }
508        };
509
510        let put_ood_row_pointers_back_on_stack = triton_asm! {
511            // _
512            push {ood_pointers_alloc.read_address()}
513            read_mem {ood_pointers_alloc.num_words()}
514            pop 1
515
516            // _ *curr_main *curr_aux *next_main *next_aux
517        };
518
519        let challenges_ptr = self.memory_layout.challenges_pointer();
520
521        let assert_top_two_xfes_eq = triton_asm!(
522            // _ y2 y1 y0 x2 x1 x0
523            pick 3
524            eq
525            assert error_id 230
526
527            // _ y2 y1 x2 x1
528            pick 2
529            eq
530            assert error_id 231
531
532            // _ y2 x2
533            eq
534            assert error_id 232
535
536            // _
537        );
538
539        let main_loop_label = format!("{entrypoint}_main_loop");
540        let main_loop_body = triton_asm!(
541            //                                                        (u32, XFieldElement)
542            // _ remaining_rounds fri_gen fri_offset *etrow *btrow *qseg_elem *fri_revealed_idx *beqd_ws *oodpnts
543            // Calculate `current_fri_domain_value`
544            pick 2
545            read_mem 1
546            place 3
547            // _ remaining_rounds fri_gen fri_offset *etrow *btrow *qseg_elem *fri_revealed_xfe *beqd_ws *oodpnts fri_idx
548
549            dup 8
550            pow
551            dup 7
552            mul
553            push -1
554            mul
555            hint neg_fri_domain_point = stack[0]
556            // _ remaining_rounds fri_gen fri_offset *etrow *btrow *qseg_elem *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt)
557
558
559            dup 6
560            dup 6
561            dup 4
562            call {inner_product_three_rows_with_weights_bfe_main} // expect arguments: *aux *main *ws
563            hint main_and_aux_opened_row_element: Xfe = stack[0..3]
564            // _ remaining_rounds fri_gen fri_offset *etrow *btrow *qseg_elem *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [be_opnd_elem]
565
566            // Update `*btrow` and `*etrow` pointer values to point to previous element
567            pick 9
568            addi {-bfe!(EXTENSION_DEGREE * MasterAuxTable::NUM_COLUMNS)}
569            place 9
570            pick 8
571            addi {-bfe!(MasterMainTable::NUM_COLUMNS)}
572            place 8
573            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [be_opnd_elem]
574
575            push -1
576            xb_mul
577            hint neg_main_and_aux_opened_row_element: Xfe = stack[0..3]
578
579            // Calculate `quotient_curr_row_deep_value` for the OOD point ood^k ("p")
580            dup 4
581            {&OutOfDomainPoints::read_ood_point(OodPoint::CurrentRowPowNumSegments)}
582            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [oodp_pow_nsegs]
583
584            dup 6
585            add
586            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [oodp_pow_nsegs - fdom_pnt]
587
588            x_invert
589            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [1/(oodp_pow_nsegs - fdom_pnt)]
590
591            dup 8
592            {&quotient_segment_codeword_weights_from_be_weights}
593            dup 11
594            call {inner_product_4_xfes}
595            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [1/(oodp_pow_nsegs - fdom_pnt)] [inner_prod_p]
596
597            push -1
598            xb_mul
599            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [1/(oodp_pow_nsegs - fdom_pnt)] [-inner_prod_p]
600
601            push {ood_curr_row_quotient_segment_value_for_p_pointer_alloc.read_address()}
602            read_mem {EXTENSION_DEGREE}
603            pop 1
604            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [1/(oodp_pow_nsegs - fdom_pnt)] [-inner_prod_p] [out_of_domain_curr_row_quotient_segment_value_for_p]
605
606            xx_add
607            xx_mul
608            hint quot_curr_row_deep_value_for_p: XFieldElement = stack[0..3]
609            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [(out_of_domain_curr_row_quotient_segment_value_for_p - inner_prod_p) / (oodp_pow_nsegs - fdom_pnt)]
610            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [quot_curr_row_deep_value_for_p]
611
612
613            /* Calculate $dv2 = quot_curr_row_deep_value_for_p * deep_codeword_weights[2]$ */
614            dup 8
615            {&deep_codeword_weights_read_address(2)}
616            read_mem {EXTENSION_DEGREE}
617            pop 1
618            xx_mul
619            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [quot_curr_row_deep_value_for_p * deep_codeword_weights[2]]
620            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [dv2]
621
622
623            // Calculate `quotient_curr_row_deep_value` for the OOD point (ood·ζ)^k ("r")
624            dup 7
625            {&OutOfDomainPoints::read_ood_point(OodPoint::CurrentRowTimesZetaPowNumSegments)}
626            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [dv2] [oodp_tz_pow_nsegs]
627
628            dup 9
629            add
630            x_invert
631            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [dv2] [1/(oodp_tz_pow_nsegs - fdom_pnt)]
632
633            dup 11
634            {&quotient_segment_codeword_weights_from_be_weights}
635            addi {EXTENSION_DEGREE}
636            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [dv2] [1/(oodp_tz_pow_nsegs - fdom_pnt)] *quot_ws[1]
637
638            dup 14
639            addi {EXTENSION_DEGREE}
640            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [dv2] [1/(oodp_tz_pow_nsegs - fdom_pnt)] *quot_ws[1] *qseg_elem[1]
641
642            // Update `*qseg_elem` pointer value to point to previous element
643            pick 15
644            addi {-bfe!(NUM_RANDOMIZED_QUOTIENT_SEGMENTS * EXTENSION_DEGREE)}
645            place 15
646            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [dv2] [1/(oodp_tz_pow_nsegs - fdom_pnt)] *quot_ws[1] *qseg_elem[1]
647
648            call {inner_product_4_xfes}
649            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [dv2] [1/(oodp_tz_pow_nsegs - fdom_pnt)] [inner_prod_r]
650
651            push -1
652            xb_mul
653            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [dv2] [1/(oodp_tz_pow_nsegs - fdom_pnt)] [-inner_prod_r]
654
655            push {ood_curr_row_quotient_segment_value_for_r_pointer_alloc.read_address()}
656            read_mem {EXTENSION_DEGREE}
657            pop 1
658            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [dv2] [1/(oodp_tz_pow_nsegs - fdom_pnt)] [-inner_prod_r] [out_of_domain_curr_row_quotient_segment_value_for_r]
659
660            xx_add
661            xx_mul
662            hint quot_curr_row_deep_value_for_r: XFieldElement = stack[0..3]
663            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [dv2] [quot_curr_row_deep_value_for_r]
664
665
666            /* Calculate $dv3 = quot_curr_row_deep_value_for_r * deep_codeword_weights[3]$ */
667            dup 11
668            {&deep_codeword_weights_read_address(3)}
669            read_mem {EXTENSION_DEGREE}
670            pop 1
671            xx_mul
672            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [dv2] [dv3]
673
674            xx_add
675            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [dv2 + dv3]
676
677            dup 7
678            {&OutOfDomainPoints::read_ood_point(OodPoint::CurrentRow)}
679            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [dv2 + dv3] [ood_point_curr_row]
680
681            dup 9
682            add
683            x_invert
684            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [dv2 + dv3] [1/(ood_point_curr_row - fdom_pnt)]
685
686            push {ood_curr_row_main_and_aux_value_pointer_alloc.read_address()}
687            read_mem {EXTENSION_DEGREE}
688            pop 1
689            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [dv2 + dv3] [1/(ood_point_curr_row - fdom_pnt)] [out_of_domain_curr_row_main_and_aux_value]
690
691            dup 11
692            dup 11
693            dup 11
694            xx_add
695            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [dv2 + dv3] [1/(ood_point_curr_row - fdom_pnt)] [out_of_domain_curr_row_main_and_aux_value - be_opnd_elem]
696
697            xx_mul
698            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [dv2 + dv3] [(out_of_domain_curr_row_main_and_aux_value - be_opnd_elem)/(ood_point_curr_row - fdom_pnt)]
699
700            dup 11
701            {&deep_codeword_weights_read_address(0)}
702            read_mem {EXTENSION_DEGREE}              // read deep_codeword_weights[0]
703            pop 1
704            xx_mul
705            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [dv2 + dv3] [deep_codeword_weights[0] * (out_of_domain_curr_row_main_and_aux_value - be_opnd_elem)/(ood_point_curr_row - fdom_pnt)]
706            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [dv2 + dv3] [dv0]
707
708            xx_add
709            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [-be_opnd_elem] [dv2 + dv3 + dv0]
710            hint dv2_plus_dv3_plus_dv0: XFieldElement = stack[0..3]
711
712            pick 5
713            pick 5
714            pick 5
715            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [dv2 + dv3 + dv0] [-be_opnd_elem]
716
717            push {ood_next_row_main_and_aux_value_pointer_alloc.read_address()}
718            read_mem {EXTENSION_DEGREE}
719            pop 1
720            xx_add
721            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [dv2 + dv3 + dv0] [ood_next_row_be_value - be_opnd_elem]
722
723            dup 7
724            {&OutOfDomainPoints::read_ood_point(OodPoint::NextRow)}
725            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [dv2 + dv3 + dv0] [ood_next_row_be_value - be_opnd_elem] [out_of_domain_point_next_row]
726
727            dup 9
728            add
729            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [dv2 + dv3 + dv0] [ood_next_row_be_value - be_opnd_elem] [out_of_domain_point_next_row - fdom_pnt]
730
731            x_invert
732            xx_mul
733            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [dv2 + dv3 + dv0] [(ood_next_row_be_value - be_opnd_elem)/(out_of_domain_point_next_row - fdom_pnt)]
734
735            dup 8
736            {&deep_codeword_weights_read_address(1)}
737            read_mem {EXTENSION_DEGREE}              // read deep_codeword_weights[1]
738            pop 1
739            xx_mul
740            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [dv2 + dv3 + dv0] [deep_codeword_weights[1] * (ood_next_row_be_value - be_opnd_elem)/(out_of_domain_point_next_row - fdom_pnt)]
741            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [dv2 + dv3 + dv0] [dv1]
742
743            xx_add
744            hint deep_value: XFieldElement = stack[0..3]
745            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [dv2 + dv3 + dv0 + dv1]
746            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts (-fdom_pnt) [deep_value]
747
748            pick 3
749            pop 1
750            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_xfe *beqd_ws *oodpnts [deep_value]
751
752            pick 5
753            read_mem {EXTENSION_DEGREE}
754            place 8
755            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_idx_prev *beqd_ws *oodpnts [deep_value] [fri_revealed_value]
756
757            {&assert_top_two_xfes_eq}
758
759            // _ remaining_rounds fri_gen fri_offset *etrow_prev *btrow_prev *qseg_elem_prev *fri_revealed_idx_prev *beqd_ws *oodpnts
760        );
761        let main_loop = triton_asm!(
762            // The loop goes from last index to 1st index
763            // Invariant: _ num_colli fri_gen fri_offset *etrow *btrow *qseg_elem *fri_revealed_elem *beqd_ws *oodpnts
764            {main_loop_label}:
765                // test end-condition
766                dup 8
767                push 0
768                eq
769                skiz
770                    return
771
772                {&main_loop_body}
773
774                // Update counter
775                pick 8
776                addi -1
777                place 8
778                recurse
779        );
780
781        triton_asm!(
782            {entrypoint}:
783                sponge_init
784                // _ *claim *proof
785
786                call {proof_to_vm_proof_iter}
787                hint proof_iter = stack[0]
788
789                // _ *clm *proof_iter
790
791
792                /* Fiat-Shamir: Claim */
793                dup 1
794                call {instantiate_fiat_shamir_with_claim}
795                // _ *clm *p_iter
796
797
798                /* derive additional parameters */
799                dup 0
800                call {next_as_log_2_padded_height}
801                // _ *clm *p_iter *log_2_padded_height
802
803                read_mem 1
804                pop 1
805                // _ *clm *p_iter log_2_padded_height
806
807                /* Verify log_2_padded_height <= 31 && log_2_padded_height >= 8 */
808                push 32
809                dup 1
810                lt
811                // _ *clm *p_iter log_2_padded_height (32 > log_2_padded_height)
812
813                assert error_id {Self::LOG2_PADDED_HEIGHT_TOO_LARGE}
814                // _ *clm *p_iter log_2_padded_height
815
816                push 8
817                dup 1
818                lt
819                push 0
820                eq
821                // _ *clm *p_iter log_2_padded_height (8 <= log_2_padded_height)
822
823                assert error_id {Self::LOG2_PADDED_HEIGHT_TOO_SMALL}
824
825
826                push 2
827                pow
828                hint padded_height = stack[0]
829                // _ *clm *p_iter padded_height
830
831                dup 0
832                call {derive_fri_parameters}
833                hint fri = stack[0]
834                // _ *clm *p_iter padded_height *fri
835
836                /* Replace `padded_height` with the trace domain length, which
837                   all later consumers of that stack slot (trace domain
838                   generator, zerofiers) actually need. The trace domain is
839                   exactly half the randomized trace domain, whose length in
840                   turn is the FRI domain length divided by the expansion
841                   factor. Due to the lower bounds on the randomized trace
842                   length, the trace domain can be longer than the padded
843                   height. */
844                dup 0
845                {&domain_length_field}
846                read_mem 1
847                pop 1
848                // _ *clm *p_iter padded_height *fri fri_domain_length
849
850                push {(bfe!(2) * bfe!(self.stark.fri_expansion_factor as u64)).inverse()}
851                mul
852                hint trace_domain_len = stack[0]
853                // _ *clm *p_iter padded_height *fri trace_domain_len
854
855                place 2
856                pick 1
857                pop 1
858                // _ *clm *p_iter trace_domain_len *fri
859
860                /* Fiat-Shamir 1 */
861                dup 2
862                call {next_as_merkleroot}
863                hint b_mr = stack[0]
864                // _ *clm *p_iter trace_domain_len *fri *b_mr
865
866                swap 4
867                // _ *b_mr *p_iter trace_domain_len *fri *clm
868
869                call {get_challenges}
870                // _ *b_mr *p_iter trace_domain_len *fri *challenges
871
872                // verify that the challenges are stored at the right place
873                push {challenges_ptr}
874                eq
875                assert error_id 233
876                // _ *b_mr *p_iter trace_domain_len *fri
877
878                dup 2
879                call {next_as_merkleroot}
880                hint e_mr = stack[0]
881                // _ *b_mr *p_iter trace_domain_len *fri *e_mr
882
883                call {sample_quotient_codeword_weights}
884                // _ *b_mr *p_iter trace_domain_len *fri *e_mr *quot_cw_ws
885                hint quot_codeword_weights = stack[0]
886
887                dup 4
888                call {next_as_merkleroot}
889                hint quot_mr = stack[0]
890                // _ *b_mr *p_iter trace_domain_len *fri *e_mr *quot_cw_ws *quot_mr
891
892
893                /* sample and calculate OOD points (not rows) */
894                push 0
895                dup 5
896                call {domain_generator}
897                hint trace_domain_generator = stack[0]
898                // _ *b_mr *p_iter trace_domain_len *fri *e_mr *quot_cw_ws *quot_mr dom_gen
899
900                dup 0
901                // _ *b_mr *p_iter trace_domain_len *fri *e_mr *quot_cw_ws *quot_mr dom_gen dom_gen
902
903                call {sample_scalar_one}
904                // _ *b_mr *p_iter trace_domain_len *fri *e_mr *quot_cw_ws *quot_mr dom_gen dom_gen [ood_curr_row]
905
906                call {calculate_out_of_domain_points}
907                hint out_of_domain_points = stack[0]
908                // _ *b_mr *p_iter trace_domain_len *fri *e_mr *quot_cw_ws *quot_mr dom_gen *oodpnts
909
910
911                /* out-of-domain quotient summands */
912                push 2
913                add
914                read_mem {EXTENSION_DEGREE}
915                push 1
916                add
917                // _ *b_mr *p_iter trace_domain_len *fri *e_mr *quot_cw_ws *quot_mr dom_gen [out_of_domain_curr_row] *oodpnts
918
919                swap 9
920                // _ *b_mr *p_iter *oodpnts *fri *e_mr *quot_cw_ws *quot_mr dom_gen [out_of_domain_curr_row] trace_domain_len
921
922                dup 10
923                {&dequeue_four_ood_rows}
924                // _ *b_mr *p_iter *oodpnts *fri *e_mr *quot_cw_ws *quot_mr dom_gen [out_of_domain_curr_row] trace_domain_len *proof_iter *curr_main *curr_aux *next_main *next_aux
925
926                {&evaluate_air_and_store_ood_pointers}
927                // _ *b_mr *p_iter *oodpnts *fri *e_mr *quot_cw_ws *quot_mr dom_gen [out_of_domain_curr_row] trace_domain_len *air_evaluation_result
928
929                swap 5
930                // _ *b_mr *p_iter *oodpnts *fri *e_mr *quot_cw_ws *quot_mr *air_evaluation_result [out_of_domain_curr_row] trace_domain_len dom_gen
931
932                call {divide_out_zerofiers}
933                // _ *b_mr *p_iter *oodpnts *fri *e_mr *quot_cw_ws *quot_mr *quotient_summands
934
935                {&put_ood_row_pointers_back_on_stack}
936                // _ *b_mr *p_iter *oodpnts *fri *e_mr *quot_cw_ws *quot_mr *quotient_summands *ood_brow_curr *ood_erow_curr *odd_brow_nxt *ood_erow_nxt
937
938                dup 10
939                call {next_as_outofdomainquotientsegments}
940                hint out_of_domain_quot_segments_for_p: Pointer = stack[0]
941                // _ *b_mr *p_iter *oodpnts *fri *e_mr *quot_cw_ws *quot_mr *quotient_summands *ood_brow_curr *ood_erow_curr *odd_brow_nxt *ood_erow_nxt *ood_quot_segments_p
942
943                dup 0
944                push {ood_curr_row_quot_segments_for_p_pointer_alloc.write_address()}
945                write_mem {ood_curr_row_quot_segments_for_p_pointer_alloc.num_words()}
946                pop 1
947
948                dup 11
949                call {next_as_outofdomainquotientsegments}
950                hint out_of_domain_quot_segments_for_r: Pointer = stack[0]
951                // _ *b_mr *p_iter *oodpnts *fri *e_mr *quot_cw_ws *quot_mr *quotient_summands *ood_brow_curr *ood_erow_curr *odd_brow_nxt *ood_erow_nxt *ood_quot_segments_p *ood_quot_segments_r
952
953                dup 0
954                push {ood_curr_row_quot_segments_for_r_pointer_alloc.write_address()}
955                write_mem {ood_curr_row_quot_segments_for_r_pointer_alloc.num_words()}
956                pop 1
957
958
959                /* Calculate the derandomized out-of-domain quotient value:
960                   Horner(p_row, ood_curr_row) + Horner(r_row, ood_curr_row·ζ) */
961                dup 11
962                {&OutOfDomainPoints::read_ood_point(OodPoint::CurrentRow)}
963                // _ *b_mr *p_iter *oodpnts *fri *e_mr *quot_cw_ws *quot_mr *quotient_summands *ood_brow_curr *ood_erow_curr *odd_brow_nxt *ood_erow_nxt *ood_quot_segments_p *ood_quot_segments_r [ood_curr_row]
964
965                push {Stark::ZETA}
966                xb_mul
967                // _ *b_mr *p_iter *oodpnts *fri *e_mr *quot_cw_ws *quot_mr *quotient_summands *ood_brow_curr *ood_erow_curr *odd_brow_nxt *ood_erow_nxt *ood_quot_segments_p *ood_quot_segments_r [ood_curr_row·ζ]
968
969                call {horner_evaluation_of_ood_curr_row_quot_segments}
970                // _ *b_mr *p_iter *oodpnts *fri *e_mr *quot_cw_ws *quot_mr *quotient_summands *ood_brow_curr *ood_erow_curr *odd_brow_nxt *ood_erow_nxt *ood_quot_segments_p [sum_r]
971
972                pick 3
973                // _ *b_mr *p_iter *oodpnts *fri *e_mr *quot_cw_ws *quot_mr *quotient_summands *ood_brow_curr *ood_erow_curr *odd_brow_nxt *ood_erow_nxt [sum_r] *ood_quot_segments_p
974
975                dup 13
976                {&OutOfDomainPoints::read_ood_point(OodPoint::CurrentRow)}
977                // _ *b_mr *p_iter *oodpnts *fri *e_mr *quot_cw_ws *quot_mr *quotient_summands *ood_brow_curr *ood_erow_curr *odd_brow_nxt *ood_erow_nxt [sum_r] *ood_quot_segments_p [ood_curr_row]
978
979                call {horner_evaluation_of_ood_curr_row_quot_segments}
980                // _ *b_mr *p_iter *oodpnts *fri *e_mr *quot_cw_ws *quot_mr *quotient_summands *ood_brow_curr *ood_erow_curr *odd_brow_nxt *ood_erow_nxt [sum_r] [sum_p]
981
982                xx_add
983                // _ *b_mr *p_iter *oodpnts *fri *e_mr *quot_cw_ws *quot_mr *quotient_summands *ood_brow_curr *ood_erow_curr *odd_brow_nxt *ood_erow_nxt [sum_of_evaluated_out_of_domain_quotient_segments]
984
985
986                /* Calculate inner product `out_of_domain_quotient_value` */
987                pick 4 place 9
988                pick 3 place 6
989                pick 8 pick 6
990                // _ *b_mr *p_iter *oodpnts *fri *e_mr *odd_brow_nxt *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr [sum_of_evaluated_out_of_domain_quotient_segments] *quot_cw_ws *quotient_summands
991
992                call {inner_product_quotient_summands}
993                // _ *b_mr *p_iter *oodpnts *fri *e_mr *odd_brow_nxt *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr [sum_of_evaluated_out_of_domain_quotient_segments] [out_of_domain_quotient_value]
994
995                /* Verify quotient's segments */
996                {&assert_top_two_xfes_eq}
997                // _ *b_mr *p_iter *oodpnts *fri *e_mr *odd_brow_nxt *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr
998
999                /* Fiat-shamir 2 */
1000                call {sample_beqd_weights}
1001                hint beqd_weights = stack[0]
1002                // _ *b_mr *p_iter *oodpnts *fri *e_mr *odd_brow_nxt *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr *beqd_ws
1003
1004                swap 10
1005                // _ *beqd_ws *p_iter *oodpnts *fri *e_mr *odd_brow_nxt *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr *b_mr
1006
1007
1008                /* FRI */
1009                // We need the `fri` data structure for field values later, so we preserve its pointer on the stack
1010                dup 9
1011                dup 8
1012                call {fri_verify}
1013                hint fri_revealed = stack[0]
1014                // _ *beqd_ws *p_iter *oodpnts *fri *e_mr *odd_brow_nxt *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr *b_mr *fri_revealed
1015
1016
1017                /* Dequeue main-table rows and verify against its Merkle root */
1018                dup 10
1019                call {next_as_maintablerows}
1020                // _ *beqd_ws *p_iter *oodpnts *fri *e_mr *odd_brow_nxt *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr *b_mr *fri_revealed *btrows
1021
1022
1023                dup 9
1024                {&num_collinearity_checks_field}
1025                read_mem 1
1026                pop 1
1027                // _ *beqd_ws *p_iter *oodpnts *fri *e_mr *odd_brow_nxt *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr *b_mr *fri_revealed *btrows num_colli
1028
1029                dup 10
1030                {&domain_length_field}
1031                read_mem 1
1032                pop 1
1033                // _ *beqd_ws *p_iter *oodpnts *fri *e_mr *odd_brow_nxt *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr *b_mr *fri_revealed *btrows num_colli dom_len
1034
1035                log_2_floor
1036                // _ *beqd_ws *p_iter *oodpnts *fri *e_mr *odd_brow_nxt *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr *b_mr *fri_revealed *btrows num_colli mt_height
1037
1038                pick 4
1039                // _ *beqd_ws *p_iter *oodpnts *fri *e_mr *odd_brow_nxt *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr *fri_revealed *btrows num_colli mt_height *b_mr
1040
1041                dup 4
1042                // _ *beqd_ws *p_iter *oodpnts *fri *e_mr *odd_brow_nxt *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr *fri_revealed *btrows num_colli mt_height *b_mr *fri_revealed
1043
1044                dup 4
1045                // _ *beqd_ws *p_iter *oodpnts *fri *e_mr *odd_brow_nxt *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr *fri_revealed *btrows num_colli mt_height *b_mr *fri_revealed *btrows
1046
1047                call {verify_main_table_rows}
1048                // _ *beqd_ws *p_iter *oodpnts *fri *e_mr *odd_brow_nxt *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr *fri_revealed *btrows
1049
1050
1051                /* Dequeue and ignore main-table's authentication path */
1052                dup 10
1053                call {next_as_authentication_path}
1054                pop 1
1055                // _ *beqd_ws *p_iter *oodpnts *fri *e_mr *odd_brow_nxt *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr *fri_revealed *btrows
1056
1057                swap 7
1058                // _ *beqd_ws *p_iter *oodpnts *fri *btrows *odd_brow_nxt *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr *fri_revealed *e_mr
1059
1060
1061                /* Dequeue aux-table rows and verify against its Merkle root */
1062                dup 10
1063                call {next_as_auxtablerows}
1064                // _ *beqd_ws *p_iter *oodpnts *fri *btrows *odd_brow_nxt *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr *fri_revealed *e_mr *etrows
1065
1066                pick 1
1067                // _ *beqd_ws *p_iter *oodpnts *fri *btrows *odd_brow_nxt *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr *fri_revealed *etrows *e_mr
1068
1069                dup 9
1070                {&num_collinearity_checks_field}
1071                read_mem 1
1072                pop 1
1073                dup 10
1074                {&domain_length_field}
1075                read_mem 1
1076                pop 1
1077                log_2_floor
1078                // _ *beqd_ws *p_iter *oodpnts *fri *btrows *odd_brow_next *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr *fri_revealed *etrows *e_mr num_colli mt_height
1079
1080                pick 2
1081                dup 4
1082                dup 4
1083                // _ *beqd_ws *p_iter *oodpnts *fri *btrows *odd_brow_next *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr *fri_revealed *etrows num_colli mt_height *e_mr *fri_revealed *etrows
1084
1085                call {verify_aux_table_rows}
1086                // _ *beqd_ws *p_iter *oodpnts *fri *btrows *odd_brow_next *quot_mr *ood_erow_nxt *ood_brow_curr *ood_erow_curr *fri_revealed *etrows
1087
1088
1089                /* Dequeue and ignore aux-table's authentication path */
1090                dup 10
1091                call {next_as_authentication_path}
1092                pop 1
1093
1094                swap 5
1095                // _ *beqd_ws *p_iter *oodpnts *fri *btrows *odd_brow_next *etrows *ood_erow_nxt *ood_brow_curr *ood_erow_curr *fri_revealed *quot_mr
1096
1097
1098                /* Dequeue quotient-table rows and verify against its Merkle root */
1099                dup 10
1100                call {next_as_quotient_segment_elements}
1101                swap 1
1102                dup 9
1103                {&num_collinearity_checks_field}
1104                read_mem 1
1105                pop 1
1106                dup 10
1107                {&domain_length_field}
1108                read_mem 1
1109                pop 1
1110                log_2_floor
1111                // _ *beqd_ws *p_iter *oodpnts *fri *btrows *odd_brow_next *etrows *ood_erow_nxt *ood_brow_curr *ood_erow_curr *fri_revealed *qseg_elems *quot_mr num_colli mt_height
1112
1113                pick 2
1114                // _ *beqd_ws *p_iter *oodpnts *fri *btrows *odd_brow_next *etrows *ood_erow_nxt *ood_brow_curr *ood_erow_curr *fri_revealed *qseg_elems num_colli mt_height *quot_mr
1115
1116                dup 4
1117                dup 4
1118                // _ *beqd_ws *p_iter *oodpnts *fri *btrows *odd_brow_next *etrows *ood_erow_nxt *ood_brow_curr *ood_erow_curr *fri_revealed *qseg_elems num_colli mt_height *quot_mr *fri_revealed *qseg_elems
1119
1120                call {verify_quotient_segments}
1121                // _ *beqd_ws *p_iter *oodpnts *fri *btrows *odd_brow_next *etrows *ood_erow_nxt *ood_brow_curr *ood_erow_curr *fri_revealed *qseg_elems
1122
1123                /* Various length asserts */
1124                // assert!(num_combination_codeword_checks == quotient_segment_elements.len());
1125                dup 8
1126                {&num_collinearity_checks_field}
1127                read_mem 1
1128                pop 1
1129                hint num_combination_codeword_checks = stack[0]
1130                // _ *beqd_ws *p_iter *oodpnts *fri *btrows *odd_brow_next *etrows *ood_erow_nxt *ood_brow_curr *ood_erow_curr *fri_revealed *qseg_elems num_colli
1131
1132                // assert!(num_combination_codeword_checks == revealed_fri_indices_and_elements.len())
1133                dup 2
1134                read_mem 1
1135                pop 1
1136                dup 1
1137                eq
1138                assert error_id 234
1139                // _ *beqd_ws *p_iter *oodpnts *fri *btrows *odd_brow_next *etrows *ood_erow_nxt *ood_brow_curr *ood_erow_curr *fri_revealed *qseg_elems num_colli
1140
1141                // assert!(num_combination_codeword_checks == main_table_rows.len());
1142                dup 8
1143                read_mem 1
1144                pop 1
1145                dup 1
1146                eq
1147                assert error_id 235
1148
1149                // assert!(num_combination_codeword_checks == aux_table_rows.len())
1150                dup 6
1151                read_mem 1
1152                pop 1
1153                dup 1
1154                eq
1155                assert error_id 236
1156
1157                // assert!(num_combination_codeword_checks == quotient_segment_elements.len());
1158                dup 1
1159                read_mem 1
1160                pop 1
1161                dup 1
1162                eq
1163                assert error_id 237
1164                // _ *beqd_ws *p_iter *oodpnts *fri *btrows *odd_brow_next *etrows *ood_erow_nxt *ood_brow_curr *ood_erow_curr *fri_revealed *qseg_elems num_colli
1165
1166
1167                /* Dequeue last authentication path, and verify that p_iter ends up in consistent state */
1168                swap 12
1169                swap 11
1170                dup 0
1171                call {next_as_authentication_path}
1172                pop 1
1173
1174                call {drop_vm_proof_iter}
1175                // _ num_colli *beqd_ws *oodpnts *fri *btrows *odd_brow_next *etrows *ood_erow_nxt *ood_brow_curr *ood_erow_curr *fri_revealed *qseg_elems
1176
1177                /* Sum out-of-domain values */
1178                // Goal for stack: `_ *ood_erow_curr *ood_brow_curr *beqd_ws`, preserving `*beqd_ws`.
1179
1180                dup 10
1181                swap 2
1182                // _ num_colli *beqd_ws *oodpnts *fri *btrows *odd_brow_next *etrows *ood_erow_nxt *ood_brow_curr *ood_erow_curr *beqd_ws *qseg_elems *fri_revealed
1183
1184                swap 4
1185                swap 1
1186                swap 3
1187                swap 2
1188                // _ num_colli *beqd_ws *oodpnts *fri *btrows *odd_brow_next *etrows *ood_erow_nxt *fri_revealed *qseg_elems *ood_erow_curr *ood_brow_curr *beqd_ws
1189
1190                call {inner_product_three_rows_with_weights_xfe_main} // expects arguments: *aux *main *ws
1191                hint out_of_domain_curr_row_main_and_aux_value: XFieldElement = stack[0..3]
1192                // _ num_colli *beqd_ws *oodpnts *fri *btrows *odd_brow_next *etrows *ood_erow_nxt *fri_revealed *qseg_elems [ood_curr_beval]
1193
1194                push {ood_curr_row_main_and_aux_value_pointer_alloc.write_address()}
1195                write_mem {ood_curr_row_main_and_aux_value_pointer_alloc.num_words()}
1196                pop 1
1197                // _ num_colli *beqd_ws *oodpnts *fri *btrows *odd_brow_next *etrows *ood_erow_nxt *fri_revealed *qseg_elems
1198
1199                // Goal: `_ *ood_erow_nxt *odd_brow_next *beqd_ws`, preserving `*beqd_ws`.
1200
1201                swap 2
1202                swap 1
1203                swap 4
1204                dup 8
1205                // _ num_colli *beqd_ws *oodpnts *fri *btrows *fri_revealed *etrows *qseg_elems *ood_erow_nxt *odd_brow_next *beqd_ws
1206
1207                call {inner_product_three_rows_with_weights_xfe_main}  // expects arguments: *aux *main *ws
1208                hint out_of_domain_next_row_main_and_aux_value: XFieldElement = stack[0..3]
1209                // _ num_colli *beqd_ws *oodpnts *fri *btrows *fri_revealed *etrows *qseg_elems [ood_next_value]
1210
1211                push {ood_next_row_main_and_aux_value_pointer_alloc.write_address()}
1212                write_mem {ood_next_row_main_and_aux_value_pointer_alloc.num_words()}
1213                pop 1
1214                // _ num_colli *beqd_ws *oodpnts *fri *btrows *fri_revealed *etrows *qseg_elems
1215
1216                // Goal: `_ *quotient_segment_codeword_weights *ood_curr_row_quot_segments_p`
1217                dup 6
1218                {&quotient_segment_codeword_weights_from_be_weights}
1219                // _ num_colli *beqd_ws *oodpnts *fri *btrows *fri_revealed *etrows *qseg_elems *quotient_segment_codeword_weights
1220
1221                push {ood_curr_row_quot_segments_for_p_pointer_alloc.read_address()}
1222                read_mem {ood_curr_row_quot_segments_for_p_pointer_alloc.num_words()}
1223                pop 1
1224                // _ num_colli *beqd_ws *oodpnts *fri *btrows *fri_revealed *etrows *qseg_elems *quotient_segment_codeword_weights *ood_curr_row_quot_segments_p
1225
1226                call {inner_product_4_xfes}
1227                hint out_of_domain_curr_row_quotient_segment_value_for_p: XFieldElement = stack[0..3]
1228                // _ num_colli *beqd_ws *oodpnts *fri *btrows *fri_revealed *etrows *qseg_elems [out_of_domain_curr_row_quotient_segment_value_for_p]
1229
1230                push {ood_curr_row_quotient_segment_value_for_p_pointer_alloc.write_address()}
1231                write_mem {ood_curr_row_quotient_segment_value_for_p_pointer_alloc.num_words()}
1232                pop 1
1233                // _ num_colli *beqd_ws *oodpnts *fri *btrows *fri_revealed *etrows *qseg_elems
1234
1235                // The "r" value weighs the r-row with quotient-segment weights 1..=4
1236                dup 6
1237                {&quotient_segment_codeword_weights_from_be_weights}
1238                addi {EXTENSION_DEGREE}
1239                // _ num_colli *beqd_ws *oodpnts *fri *btrows *fri_revealed *etrows *qseg_elems *quotient_segment_codeword_weights[1]
1240
1241                push {ood_curr_row_quot_segments_for_r_pointer_alloc.read_address()}
1242                read_mem {ood_curr_row_quot_segments_for_r_pointer_alloc.num_words()}
1243                pop 1
1244                // _ num_colli *beqd_ws *oodpnts *fri *btrows *fri_revealed *etrows *qseg_elems *quotient_segment_codeword_weights[1] *ood_curr_row_quot_segments_r
1245
1246                call {inner_product_4_xfes}
1247                hint out_of_domain_curr_row_quotient_segment_value_for_r: XFieldElement = stack[0..3]
1248                // _ num_colli *beqd_ws *oodpnts *fri *btrows *fri_revealed *etrows *qseg_elems [out_of_domain_curr_row_quotient_segment_value_for_r]
1249
1250                push {ood_curr_row_quotient_segment_value_for_r_pointer_alloc.write_address()}
1251                write_mem {ood_curr_row_quotient_segment_value_for_r_pointer_alloc.num_words()}
1252                pop 1
1253                // _ num_colli *beqd_ws *oodpnts *fri *btrows *fri_revealed *etrows *qseg_elems
1254
1255                // Put fri domain generator and domain offset on stack
1256                swap 4
1257                dup 0
1258                {&domain_offset_field}
1259                read_mem 1
1260                pop 1
1261                hint fri_domain_offset = stack[0]
1262                // _ num_colli *beqd_ws *oodpnts *qseg_elems *btrows *fri_revealed *etrows *fri fri_offset
1263
1264                swap 1
1265                {&domain_generator_field}
1266                read_mem 1
1267                pop 1
1268                hint fri_domain_gen = stack[0]
1269                // _ num_colli *beqd_ws *oodpnts *qseg_elems *btrows *fri_revealed *etrows fri_offset fri_gen
1270
1271                // adjust relevant pointers to point to last word in sequence, as they are traversed
1272                // high-to-low in the main loop
1273
1274                // Adjust *fri_revealed (list) to point to last word
1275                pick 3
1276                dup 8
1277                push {EXTENSION_DEGREE + 1} // size of element in `fri_revealed` list
1278                mul
1279                add
1280                hint fri_revealed_elem = stack[0]
1281                place 3
1282
1283                // Adjust *btrows (list) to point to last element
1284                pick 4
1285                addi 1
1286                dup 8
1287                addi -1
1288                push {MasterMainTable::NUM_COLUMNS} // size of element of main row list
1289                mul
1290                add
1291                hint main_table_row = stack[0]
1292                place 4
1293
1294                // Adjust *etrows (list) to point to last element
1295                pick 2
1296                addi 1
1297                dup 8
1298                addi -1
1299                push {MasterAuxTable::NUM_COLUMNS * EXTENSION_DEGREE} // size of element of aux row list
1300                mul
1301                add
1302                hint aux_table_row = stack[0]
1303                place 2
1304
1305                // Adjust *qseg_elems to point to last element
1306                pick 5
1307                addi 1
1308                dup 8
1309                addi -1
1310                push {NUM_RANDOMIZED_QUOTIENT_SEGMENTS * EXTENSION_DEGREE} // size of element of quot row list
1311                mul
1312                add
1313                hint quotient_segment_elem = stack[0]
1314                place 5
1315
1316                // _ num_colli *beqd_ws *oodpnts *qseg_elems *btrows *fri_revealed *etrows fri_offset fri_gen
1317
1318                /* reorganize stack for main-loop */
1319                place 7
1320                place 6
1321                place 5
1322                place 4
1323                place 4
1324                place 3
1325                // _ num_colli fri_gen fri_offset *etrows_last_elem *btrows_last_elem *qseg_elems_last_elem *fri_revealed_last_elem *beqd_ws *oodpnts
1326
1327                call {main_loop_label}
1328                // _ 0 fri_gen fri_offset *etrow_elem *btrows_elem *qseg_elem *fri_revealed_elem *beqd_ws *oodpnts
1329
1330                /* Cleanup stack */
1331                pop 5 pop 4
1332
1333                return
1334
1335                {&main_loop}
1336        )
1337    }
1338}
1339
1340#[cfg(test)]
1341pub mod tests {
1342    use std::collections::HashMap;
1343
1344    use num_traits::ConstZero;
1345    use tasm_object_derive::TasmObject;
1346    use triton_vm::proof_item::ProofItem;
1347
1348    use super::*;
1349    use crate::execute_test;
1350    use crate::maybe_write_debuggable_vm_state_to_disk;
1351    use crate::memory::FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS;
1352    use crate::test_helpers::maybe_write_tvm_output_to_disk;
1353    use crate::test_prelude::*;
1354    use crate::verifier::claim::shared::insert_claim_into_static_memory;
1355    use crate::verifier::master_table::air_constraint_evaluation::an_integral_but_profane_dynamic_memory_layout;
1356
1357    #[ignore = "Used for debugging when comparing two versions of the verifier"]
1358    #[macro_rules_attr::apply(test)]
1359    fn verify_from_stored_proof_output() {
1360        use std::fs::File;
1361        let stark = File::open("stark.json").expect("stark file should open read only");
1362        let stark: Stark = serde_json::from_reader(stark).unwrap();
1363        let claim = File::open("claim.json").expect("claim file should open read only");
1364        let claim_for_proof: Claim = serde_json::from_reader(claim).unwrap();
1365        let proof = File::open("proof.json").expect("proof file should open read only");
1366        let proof: Proof = serde_json::from_reader(proof).unwrap();
1367
1368        let snippet = StarkVerify {
1369            stark,
1370            memory_layout: MemoryLayout::conventional_dynamic(),
1371        };
1372        let mut nondeterminism = NonDeterminism::new(vec![]);
1373        snippet.update_nondeterminism(&mut nondeterminism, &proof, &claim_for_proof);
1374
1375        let (claim_pointer, claim_size) =
1376            insert_claim_into_static_memory(&mut nondeterminism.ram, &claim_for_proof);
1377
1378        let default_proof_pointer = BFieldElement::ZERO;
1379
1380        let mut init_stack = [
1381            snippet.init_stack_for_isolated_run(),
1382            vec![claim_pointer, default_proof_pointer],
1383        ]
1384        .concat();
1385        let code = snippet.link_for_isolated_run_populated_static_memory(claim_size);
1386        let _final_tasm_state = execute_test(
1387            &code,
1388            &mut init_stack,
1389            snippet.stack_diff(),
1390            vec![],
1391            nondeterminism,
1392            None,
1393        );
1394    }
1395
1396    fn vm_state_for_log2_padded_height_proof_element(log2_padded_height: u32) -> VMState {
1397        let mut proof_stream = ProofStream::new();
1398        proof_stream.enqueue(ProofItem::Log2PaddedHeight(log2_padded_height));
1399        let proof: Proof = proof_stream.into();
1400
1401        let mut nondeterminism = NonDeterminism::new(vec![]);
1402        encode_to_memory(
1403            &mut nondeterminism.ram,
1404            FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS,
1405            &proof,
1406        );
1407        let (claim_pointer, claim_size) = insert_claim_into_static_memory(
1408            &mut nondeterminism.ram,
1409            &Claim::new(Digest::default()),
1410        );
1411
1412        let snippet = StarkVerify {
1413            stark: Stark::default(),
1414            memory_layout: MemoryLayout::conventional_static(),
1415        };
1416        let init_stack = [
1417            snippet.init_stack_for_isolated_run(),
1418            vec![
1419                claim_pointer,
1420                FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS,
1421            ],
1422        ]
1423        .concat();
1424
1425        let program =
1426            Program::new(&snippet.link_for_isolated_run_populated_static_memory(claim_size));
1427        let mut vm_state = VMState::new(program, [].into(), nondeterminism.clone());
1428        vm_state.op_stack.stack = init_stack.clone();
1429
1430        vm_state
1431    }
1432
1433    #[macro_rules_attr::apply(test)]
1434    fn fail_on_too_big_log2_padded_height() {
1435        for too_big_padded_height in [32, 33, 40, 41, 999] {
1436            let mut vm_state = vm_state_for_log2_padded_height_proof_element(too_big_padded_height);
1437            let error = vm_state.run().unwrap_err();
1438            match error {
1439                InstructionError::AssertionFailed(assertion_error) => {
1440                    assert_eq!(
1441                        StarkVerify::LOG2_PADDED_HEIGHT_TOO_LARGE,
1442                        assertion_error.id.unwrap()
1443                    );
1444                }
1445                _ => panic!(),
1446            }
1447        }
1448    }
1449
1450    #[macro_rules_attr::apply(test)]
1451    fn fail_on_too_small_log2_padded_height() {
1452        for too_small_padded_height in 0..8 {
1453            let mut vm_state =
1454                vm_state_for_log2_padded_height_proof_element(too_small_padded_height);
1455            let error = vm_state.run().unwrap_err();
1456            match error {
1457                InstructionError::AssertionFailed(assertion_error) => {
1458                    assert_eq!(
1459                        StarkVerify::LOG2_PADDED_HEIGHT_TOO_SMALL,
1460                        assertion_error.id.unwrap()
1461                    );
1462                }
1463                _ => panic!(),
1464            }
1465        }
1466    }
1467
1468    /// Run the verifier, and return the cycle count and inner padded
1469    /// height for crude benchmarking.
1470    fn test_verify_and_report_basic_features(
1471        inner_nondeterminism: NonDeterminism,
1472        inner_program: Program,
1473        inner_public_input: &[BFieldElement],
1474        stark: Stark,
1475        layout: MemoryLayout,
1476    ) -> (usize, usize) {
1477        let (mut non_determinism, claim_for_proof) = prove_and_get_non_determinism_and_claim(
1478            inner_program.clone(),
1479            inner_public_input,
1480            inner_nondeterminism.clone(),
1481            &stark,
1482        );
1483
1484        let (claim_pointer, claim_size) =
1485            insert_claim_into_static_memory(&mut non_determinism.ram, &claim_for_proof);
1486
1487        let default_proof_pointer = bfe!(0);
1488
1489        let snippet = StarkVerify {
1490            stark,
1491            memory_layout: layout,
1492        };
1493        let mut init_stack = [
1494            snippet.init_stack_for_isolated_run(),
1495            vec![claim_pointer, default_proof_pointer],
1496        ]
1497        .concat();
1498        let code = snippet.link_for_isolated_run_populated_static_memory(claim_size);
1499
1500        let program = Program::new(&code);
1501        let mut vm_state = VMState::new(program, [].into(), non_determinism.clone());
1502        vm_state.op_stack.stack = init_stack.clone();
1503        maybe_write_debuggable_vm_state_to_disk(&vm_state);
1504
1505        let final_tasm_state = execute_test(
1506            &code,
1507            &mut init_stack,
1508            snippet.stack_diff(),
1509            vec![],
1510            non_determinism,
1511            None,
1512        )
1513        .unwrap();
1514
1515        let (aet, _public_output) = VM::trace_execution(
1516            inner_program,
1517            (&claim_for_proof.input).into(),
1518            inner_nondeterminism,
1519        )
1520        .unwrap();
1521        let inner_padded_height = aet.padded_height();
1522
1523        (final_tasm_state.cycle_count as usize, inner_padded_height)
1524    }
1525
1526    #[macro_rules_attr::apply(test)]
1527    fn different_fri_expansion_factors() {
1528        const FACTORIAL_ARGUMENT: u32 = 3;
1529
1530        for log2_of_fri_expansion_factor in 2..=5 {
1531            println!("log2_of_fri_expansion_factor: {log2_of_fri_expansion_factor}");
1532            let factorial_program = factorial_program_with_io();
1533            let stark = Stark::new(160, log2_of_fri_expansion_factor);
1534            let (cycle_count, inner_padded_height) = test_verify_and_report_basic_features(
1535                NonDeterminism::default(),
1536                factorial_program,
1537                &[FACTORIAL_ARGUMENT.into()],
1538                stark,
1539                MemoryLayout::conventional_static(),
1540            );
1541            println!(
1542                "TASM-verifier of factorial({FACTORIAL_ARGUMENT}):\n
1543                Fri expansion factor: {}\n
1544                clock cycle count: {}.\n
1545                Inner padded height was: {}",
1546                1 << log2_of_fri_expansion_factor,
1547                cycle_count,
1548                inner_padded_height,
1549            );
1550        }
1551    }
1552
1553    #[macro_rules_attr::apply(test)]
1554    fn verify_shortest_possible_execution() {
1555        let stark = Stark::default();
1556
1557        for memory_layout in [
1558            MemoryLayout::conventional_static(),
1559            MemoryLayout::conventional_dynamic(),
1560        ] {
1561            let program = triton_program!(halt);
1562            let (_, inner_padded_height) = test_verify_and_report_basic_features(
1563                NonDeterminism::default(),
1564                program,
1565                &[],
1566                stark,
1567                memory_layout,
1568            );
1569            assert_eq!(
1570                256, inner_padded_height,
1571                "This version of Triton VM has minimum padded height of 256"
1572            )
1573        }
1574    }
1575
1576    fn verify_tvm_proof_factorial_program_basic_properties(mem_layout: MemoryLayout) {
1577        const FACTORIAL_ARGUMENT: u32 = 3;
1578
1579        let factorial_program = factorial_program_with_io();
1580        let stark = Stark::default();
1581        let (cycle_count, inner_padded_height) = test_verify_and_report_basic_features(
1582            NonDeterminism::default(),
1583            factorial_program,
1584            &[FACTORIAL_ARGUMENT.into()],
1585            stark,
1586            mem_layout,
1587        );
1588
1589        println!(
1590            "TASM-verifier of factorial({FACTORIAL_ARGUMENT}):\n
1591            clock cycle count: {cycle_count}.\n
1592            Inner padded height was: {inner_padded_height}",
1593        );
1594    }
1595
1596    #[macro_rules_attr::apply(test)]
1597    fn verify_tvm_proof_factorial_program_conventional_static_memlayout() {
1598        verify_tvm_proof_factorial_program_basic_properties(MemoryLayout::conventional_static());
1599    }
1600
1601    #[macro_rules_attr::apply(test)]
1602    fn verify_tvm_proof_factorial_program_conventional_dynamic_memlayout() {
1603        verify_tvm_proof_factorial_program_basic_properties(MemoryLayout::conventional_dynamic());
1604    }
1605
1606    #[macro_rules_attr::apply(test)]
1607    fn verify_tvm_proof_factorial_program_profane_dynamic_memlayout() {
1608        verify_tvm_proof_factorial_program_basic_properties(MemoryLayout::Dynamic(
1609            an_integral_but_profane_dynamic_memory_layout(),
1610        ));
1611    }
1612
1613    pub(super) fn factorial_program_with_io() -> Program {
1614        triton_program!(
1615            read_io 1
1616            push 1               // n accumulator
1617            call factorial       // 0 accumulator!
1618            write_io 1
1619            halt
1620
1621            factorial:           // n acc
1622                // if n == 0: return
1623                dup 1            // n acc n
1624                push 0 eq        // n acc n==0
1625                skiz             // n acc
1626                return           // 0 acc
1627                // else: multiply accumulator with n and recurse
1628                dup 1            // n acc n
1629                mul              // n acc·n
1630                pick 1           // acc·n n
1631                addi -1          // acc·n n-1
1632                place 1          // n-1 acc·n
1633
1634                recurse
1635        )
1636    }
1637
1638    /// Return data needed to verify a program execution's proof.
1639    ///
1640    /// Prepares the caller so that the caller can call verify on a simple program
1641    /// execution. Specifically, given an inner program, inner public input, inner
1642    /// nondeterminism, and stark parameters; produce the proof, and use it to
1643    /// populate non-determism (both memory and streams). Returns the claim that
1644    /// the caller will then have to put into memory. Proof is stored on first
1645    /// address of the ND-memory region.
1646    pub fn prove_and_get_non_determinism_and_claim(
1647        inner_program: Program,
1648        inner_public_input: &[BFieldElement],
1649        inner_nondeterminism: NonDeterminism,
1650        stark: &Stark,
1651    ) -> (NonDeterminism, Claim) {
1652        println!("Generating proof for non-determinism");
1653
1654        let inner_input = inner_public_input.to_vec();
1655        let claim = Claim::about_program(&inner_program).with_input(inner_input.clone());
1656        let (aet, inner_output) =
1657            VM::trace_execution(inner_program, inner_input.into(), inner_nondeterminism).unwrap();
1658        let claim = claim.with_output(inner_output);
1659
1660        triton_vm::profiler::start("inner program");
1661        let seed = [
1662            227, 232, 115, 183, 84, 194, 68, 59, 166, 60, 140, 218, 88, 117, 227, 129, 10, 121,
1663            108, 40, 65, 125, 143, 31, 155, 128, 202, 75, 218, 44, 120, 170,
1664        ];
1665        let prover = Prover::new(*stark).set_randomness_seed_which_may_break_zero_knowledge(seed);
1666        let proof = prover.prove(&claim, &aet).unwrap();
1667        let profile = triton_vm::profiler::finish();
1668        let padded_height = proof.padded_height().unwrap();
1669        let report = profile
1670            .with_cycle_count(aet.processor_trace.nrows())
1671            .with_padded_height(padded_height)
1672            .with_fri_domain_len(stark.fri(padded_height).unwrap().domain.len());
1673        println!("Done generating proof for non-determinism");
1674        println!("{report}");
1675
1676        assert!(
1677            stark.verify(&claim, &proof).is_ok(),
1678            "Proof from TVM must verify through TVM"
1679        );
1680
1681        maybe_write_tvm_output_to_disk(stark, &claim, &proof);
1682
1683        let mut nondeterminism = NonDeterminism::new(vec![]);
1684        let stark_verify = StarkVerify {
1685            stark: *stark,
1686            memory_layout: MemoryLayout::conventional_static(),
1687        };
1688
1689        // Verify nd-digest count
1690        let actual_num_extracted_digests = stark_verify
1691            .extract_nondeterministic_digests(&proof, &claim)
1692            .len();
1693        let expected_num_extracted_digests =
1694            stark_verify.number_of_nondeterministic_digests_consumed(&proof);
1695        assert_eq!(
1696            actual_num_extracted_digests, expected_num_extracted_digests,
1697            "Number of extracted digests must match expected value"
1698        );
1699
1700        stark_verify.update_nondeterminism(&mut nondeterminism, &proof, &claim);
1701        encode_to_memory(
1702            &mut nondeterminism.ram,
1703            FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS,
1704            &proof,
1705        );
1706
1707        (nondeterminism, claim)
1708    }
1709
1710    #[macro_rules_attr::apply(test)]
1711    fn verify_two_proofs() {
1712        #[derive(Debug, Clone, BFieldCodec, TasmObject)]
1713        struct TwoProofs {
1714            proof1: Proof,
1715            claim1: Claim,
1716            proof2: Proof,
1717            claim2: Claim,
1718        }
1719
1720        let stark = Stark::default();
1721        let stark_snippet = StarkVerify::new_with_dynamic_layout(stark);
1722
1723        let mut library = Library::new();
1724        let stark_verify = library.import(Box::new(stark_snippet));
1725        let proof1 = field!(TwoProofs::proof1);
1726        let proof2 = field!(TwoProofs::proof2);
1727        let claim1 = field!(TwoProofs::claim1);
1728        let claim2 = field!(TwoProofs::claim2);
1729        let verify_two_proofs_program = triton_asm! {
1730
1731
1732            push {FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS}
1733            // *two_proofs
1734
1735            dup 0
1736            {&claim1}
1737
1738            dup 1
1739            {&proof1}
1740            // *two_proofs *claim1 *proof1
1741
1742            call {stark_verify}
1743
1744            dup 0
1745            {&claim2}
1746
1747            dup 1
1748            {&proof2}
1749            // *two_proofs *claim2 *proof2
1750
1751            call {stark_verify}
1752
1753            halt
1754
1755            {&library.all_imports()}
1756        };
1757
1758        let inner_input_1 = bfe_vec![3];
1759        let factorial_program = factorial_program_with_io();
1760        let (aet_1, inner_output_1) = VM::trace_execution(
1761            factorial_program.clone(),
1762            inner_input_1.clone().into(),
1763            [].into(),
1764        )
1765        .unwrap();
1766        let claim_1 = Claim::about_program(&factorial_program)
1767            .with_input(inner_input_1.clone())
1768            .with_output(inner_output_1.clone());
1769        let proof_1 = stark.prove(&claim_1, &aet_1).unwrap();
1770        let padded_height_1 = proof_1.padded_height().unwrap();
1771        println!("padded_height_1: {padded_height_1}");
1772
1773        let inner_input_2 = bfe_vec![25];
1774        let (aet_2, inner_output_2) = VM::trace_execution(
1775            factorial_program.clone(),
1776            inner_input_2.clone().into(),
1777            [].into(),
1778        )
1779        .unwrap();
1780        let claim_2 = Claim::about_program(&factorial_program)
1781            .with_input(inner_input_2.clone())
1782            .with_output(inner_output_2.clone());
1783        let proof_2 = stark.prove(&claim_2, &aet_2).unwrap();
1784        let padded_height_2 = proof_2.padded_height().unwrap();
1785        println!("padded_height_2: {padded_height_2}");
1786
1787        let two_proofs = TwoProofs {
1788            proof1: proof_1.clone(),
1789            claim1: claim_1.clone(),
1790            proof2: proof_2.clone(),
1791            claim2: claim_2.clone(),
1792        };
1793
1794        let mut memory = HashMap::<BFieldElement, BFieldElement>::new();
1795        let outer_input = vec![];
1796        encode_to_memory(
1797            &mut memory,
1798            FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS,
1799            &two_proofs,
1800        );
1801
1802        let mut outer_nondeterminism = NonDeterminism::new(vec![]).with_ram(memory);
1803
1804        let num_nd_digests_before = outer_nondeterminism.digests.len();
1805
1806        stark_snippet.update_nondeterminism(&mut outer_nondeterminism, &proof_1, &claim_1);
1807        stark_snippet.update_nondeterminism(&mut outer_nondeterminism, &proof_2, &claim_2);
1808
1809        let num_nd_digests_after = outer_nondeterminism.digests.len();
1810
1811        assert_eq!(
1812            num_nd_digests_after - num_nd_digests_before,
1813            stark_snippet.number_of_nondeterministic_digests_consumed(&proof_1)
1814                + stark_snippet.number_of_nondeterministic_digests_consumed(&proof_2)
1815        );
1816
1817        let program = Program::new(&verify_two_proofs_program);
1818        let vm_state = VMState::new(
1819            program.clone(),
1820            outer_input.clone().into(),
1821            outer_nondeterminism.clone(),
1822        );
1823        maybe_write_debuggable_vm_state_to_disk(&vm_state);
1824        VM::run(program, outer_input.into(), outer_nondeterminism)
1825            .expect("could not verify two STARK proofs");
1826
1827        println!(
1828            "fact({}) == {} ∧ fact({}) == {}",
1829            inner_input_1[0], inner_output_1[0], inner_input_2[0], inner_output_2[0]
1830        );
1831
1832        assert_ne!(
1833            padded_height_1, padded_height_2,
1834            "proofs do not have different padded heights"
1835        );
1836    }
1837}
1838
1839#[cfg(test)]
1840mod benches {
1841    use benches::tests::factorial_program_with_io;
1842    use benches::tests::prove_and_get_non_determinism_and_claim;
1843    use num_traits::ConstZero;
1844
1845    use super::*;
1846    use crate::generate_full_profile;
1847    use crate::linker::execute_bench;
1848    use crate::snippet_bencher::NamedBenchmarkResult;
1849    use crate::snippet_bencher::write_benchmarks;
1850    use crate::test_helpers::prepend_program_with_stack_setup;
1851    use crate::test_prelude::*;
1852    use crate::verifier::claim::shared::insert_claim_into_static_memory;
1853
1854    #[ignore = "Used for profiling the verification of a proof stored on disk."]
1855    #[macro_rules_attr::apply(test)]
1856    fn profile_from_stored_proof_output() {
1857        use std::fs::File;
1858        let stark = File::open("stark.json").expect("stark file should open read only");
1859        let stark: Stark = serde_json::from_reader(stark).unwrap();
1860        let claim_file = File::open("claim.json").expect("claim file should open read only");
1861        let claim_for_proof: Claim = serde_json::from_reader(claim_file).unwrap();
1862        let proof = File::open("proof.json").expect("proof file should open read only");
1863        let proof: Proof = serde_json::from_reader(proof).unwrap();
1864
1865        let snippet = StarkVerify {
1866            stark,
1867            memory_layout: MemoryLayout::conventional_static(),
1868        };
1869        let mut nondeterminism = NonDeterminism::new(vec![]);
1870        snippet.update_nondeterminism(&mut nondeterminism, &proof, &claim_for_proof);
1871
1872        let (claim_pointer, claim_size) =
1873            insert_claim_into_static_memory(&mut nondeterminism.ram, &claim_for_proof);
1874
1875        let default_proof_pointer = BFieldElement::ZERO;
1876
1877        let init_stack = [
1878            snippet.init_stack_for_isolated_run(),
1879            vec![claim_pointer, default_proof_pointer],
1880        ]
1881        .concat();
1882        let code = snippet.link_for_isolated_run_populated_static_memory(claim_size);
1883        let program = prepend_program_with_stack_setup(&init_stack, &Program::new(&code));
1884
1885        let name = snippet.entrypoint();
1886        let profile = generate_full_profile(
1887            &name,
1888            program,
1889            &PublicInput::new(claim_for_proof.input),
1890            &nondeterminism,
1891        );
1892        println!("{profile}");
1893    }
1894
1895    #[macro_rules_attr::apply(test)]
1896    fn benchmark_small_default_stark_static_memory() {
1897        benchmark_verifier(
1898            3,
1899            1 << 8,
1900            Stark::default(),
1901            MemoryLayout::conventional_static(),
1902        );
1903        benchmark_verifier(
1904            80,
1905            1 << 10,
1906            Stark::default(),
1907            MemoryLayout::conventional_static(),
1908        );
1909    }
1910
1911    #[macro_rules_attr::apply(test)]
1912    fn benchmark_small_default_stark_dynamic_memory() {
1913        benchmark_verifier(
1914            3,
1915            1 << 8,
1916            Stark::default(),
1917            MemoryLayout::conventional_dynamic(),
1918        );
1919        benchmark_verifier(
1920            80,
1921            1 << 10,
1922            Stark::default(),
1923            MemoryLayout::conventional_dynamic(),
1924        );
1925    }
1926
1927    #[ignore = "Takes a fairly long time. Intended to find optimal FRI expansion factor."]
1928    #[macro_rules_attr::apply(test)]
1929    fn small_benchmark_different_fri_expansion_factors() {
1930        for log2_of_fri_expansion_factor in 1..=5 {
1931            let stark = Stark::new(160, log2_of_fri_expansion_factor);
1932            benchmark_verifier(10, 1 << 8, stark, MemoryLayout::conventional_static());
1933            benchmark_verifier(40, 1 << 9, stark, MemoryLayout::conventional_static());
1934            benchmark_verifier(80, 1 << 10, stark, MemoryLayout::conventional_static());
1935        }
1936    }
1937
1938    #[ignore = "Takes a very long time. Intended to find optimal FRI expansion factor. Make sure to run
1939       with `RUSTFLAGS=\"-C opt-level=3 -C debug-assertions=no\"`"]
1940    #[macro_rules_attr::apply(test)]
1941    fn big_benchmark_different_fri_expansion_factors() {
1942        let mem_layout = MemoryLayout::conventional_static();
1943        for log2_of_fri_expansion_factor in 2..=3 {
1944            let stark = Stark::new(160, log2_of_fri_expansion_factor);
1945            benchmark_verifier(25600, 1 << 19, stark, mem_layout);
1946            benchmark_verifier(51200, 1 << 20, stark, mem_layout);
1947            benchmark_verifier(102400, 1 << 21, stark, mem_layout);
1948        }
1949    }
1950
1951    #[ignore = "Intended to generate data about verifier table heights as a function of inner padded
1952       height. Make sure to run with `RUSTFLAGS=\"-C opt-level=3 -C debug-assertions=no\"`"]
1953    #[macro_rules_attr::apply(test)]
1954    fn benchmark_verification_as_a_function_of_inner_padded_height() {
1955        for (fact_arg, expected_inner_padded_height) in [
1956            (10, 1 << 8),
1957            (40, 1 << 9),
1958            (80, 1 << 10),
1959            (100, 1 << 11),
1960            (200, 1 << 12),
1961            (400, 1 << 13),
1962            (800, 1 << 14),
1963            (1600, 1 << 15),
1964            (3200, 1 << 16),
1965            (6400, 1 << 17),
1966            (12800, 1 << 18),
1967            (25600, 1 << 19),
1968            (51200, 1 << 20),
1969            (102400, 1 << 21),
1970        ] {
1971            benchmark_verifier(
1972                fact_arg,
1973                expected_inner_padded_height,
1974                Stark::default(),
1975                MemoryLayout::conventional_static(),
1976            );
1977        }
1978    }
1979
1980    fn benchmark_verifier(
1981        factorial_argument: u32,
1982        inner_padded_height: usize,
1983        stark: Stark,
1984        mem_layout: MemoryLayout,
1985    ) {
1986        let (mut non_determinism, claim_for_proof) = prove_and_get_non_determinism_and_claim(
1987            factorial_program_with_io(),
1988            &[bfe!(factorial_argument)],
1989            NonDeterminism::default(),
1990            &stark,
1991        );
1992
1993        let claim_pointer = BFieldElement::new(1 << 30);
1994        encode_to_memory(&mut non_determinism.ram, claim_pointer, &claim_for_proof);
1995
1996        let default_proof_pointer = BFieldElement::ZERO;
1997
1998        let snippet = StarkVerify {
1999            stark,
2000            memory_layout: mem_layout,
2001        };
2002
2003        let init_stack = [
2004            snippet.init_stack_for_isolated_run(),
2005            vec![claim_pointer, default_proof_pointer],
2006        ]
2007        .concat();
2008        let code = snippet.link_for_isolated_run();
2009        let benchmark = execute_bench(&code, &init_stack, vec![], non_determinism.clone(), None);
2010        let benchmark = NamedBenchmarkResult {
2011            name: format!(
2012                "{}_inner_padded_height_{}_fri_exp_{}",
2013                snippet.entrypoint(),
2014                inner_padded_height,
2015                stark.fri_expansion_factor
2016            ),
2017            benchmark_result: benchmark,
2018            case: BenchmarkCase::CommonCase,
2019        };
2020
2021        write_benchmarks(vec![benchmark]);
2022
2023        let program = prepend_program_with_stack_setup(&init_stack, &Program::new(&code));
2024        let name = snippet.entrypoint();
2025        let profile = generate_full_profile(
2026            &name,
2027            program,
2028            &PublicInput::new(claim_for_proof.input),
2029            &non_determinism,
2030        );
2031        println!("{profile}");
2032    }
2033}