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