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