1use itertools::Itertools;
2use triton_vm::challenges::Challenges;
3use triton_vm::prelude::*;
4use triton_vm::proof_item::ProofItemVariant;
5use triton_vm::proof_stream::ProofStream;
6use triton_vm::table::NUM_QUOTIENT_SEGMENTS;
7use triton_vm::table::NUM_RANDOMIZED_QUOTIENT_SEGMENTS;
8use triton_vm::table::master_table::MasterAuxTable;
9use triton_vm::table::master_table::MasterMainTable;
10use twenty_first::math::x_field_element::EXTENSION_DEGREE;
11use twenty_first::prelude::MerkleTreeInclusionProof;
12
13use super::master_table::air_constraint_evaluation::AirConstraintEvaluation;
14use super::master_table::air_constraint_evaluation::MemoryLayout;
15use crate::arithmetic::bfe::primitive_root_of_unity::PrimitiveRootOfUnity;
16use crate::array::horner_evaluation::HornerEvaluation;
17use crate::array::inner_product_of_three_rows_with_weights::InnerProductOfThreeRowsWithWeights;
18use crate::array::inner_product_of_three_rows_with_weights::MainElementType;
19use crate::array::inner_product_of_xfes::InnerProductOfXfes;
20use crate::field;
21use crate::hashing::algebraic_hasher::sample_scalar_one::SampleScalarOne;
22use crate::hashing::algebraic_hasher::sample_scalars_static_length_dyn_malloc::SampleScalarsStaticLengthDynMalloc;
23use crate::prelude::*;
24use crate::verifier::challenges;
25use crate::verifier::claim::instantiate_fiat_shamir_with_claim::InstantiateFiatShamirWithClaim;
26use crate::verifier::claim::shared::claim_type;
27use crate::verifier::fri;
28use crate::verifier::fri::verify::FriSnippet;
29use crate::verifier::fri::verify::FriVerify;
30use crate::verifier::master_table::divide_out_zerofiers::DivideOutZerofiers;
31use crate::verifier::master_table::verify_table_rows::ColumnType;
32use crate::verifier::master_table::verify_table_rows::VerifyTableRows;
33use crate::verifier::out_of_domain_points::OodPoint;
34use crate::verifier::out_of_domain_points::OutOfDomainPoints;
35use crate::verifier::vm_proof_iter::dequeue_next_as::DequeueNextAs;
36use crate::verifier::vm_proof_iter::drop::Drop;
37use crate::verifier::vm_proof_iter::new::New;
38
39pub(crate) const NUM_PROOF_ITEMS_PER_FRI_ROUND: usize = 2;
40pub(crate) const NUM_PROOF_ITEMS_EXCLUDING_FRI: usize = 16;
41
42#[derive(Debug, Copy, Clone)]
53pub struct StarkVerify {
54 stark: Stark,
55 memory_layout: MemoryLayout,
56}
57
58impl StarkVerify {
59 const LOG2_PADDED_HEIGHT_TOO_LARGE: i128 = 238;
60 const LOG2_PADDED_HEIGHT_TOO_SMALL: i128 = 239;
61
62 pub fn new_with_static_layout(stark: Stark) -> Self {
63 Self {
64 stark,
65 memory_layout: MemoryLayout::conventional_static(),
66 }
67 }
68
69 pub fn new_with_dynamic_layout(stark: Stark) -> Self {
70 Self {
71 stark,
72 memory_layout: MemoryLayout::conventional_dynamic(),
73 }
74 }
75
76 pub fn number_of_nondeterministic_digests_consumed(&self, proof: &Proof) -> usize {
79 const NUM_FULL_DOMAIN_AUTH_PATHS: usize = 4;
80
81 let padded_height = proof.padded_height().unwrap();
82 let fri_params = self.stark.fri(padded_height).unwrap();
83 let num_fri_rounds = fri_params.num_rounds();
84
85 let mut j = 0;
86 let mut tree_height: usize = fri_params.domain.len().ilog2().try_into().unwrap();
87
88 let mut acc = NUM_FULL_DOMAIN_AUTH_PATHS * tree_height * fri_params.num_collinearity_checks;
89 while j < num_fri_rounds {
90 acc += fri_params.num_collinearity_checks * tree_height;
91 j += 1;
92 tree_height -= 1;
93 }
94
95 acc
96 }
97
98 pub fn number_of_nondeterministic_tokens_consumed(
102 &self,
103 _proof: &Proof,
104 _claim: &Claim,
105 ) -> usize {
106 0
107 }
108
109 pub fn update_nondeterminism(
113 &self,
114 nondeterminism: &mut NonDeterminism,
115 proof: &Proof,
116 claim: &Claim,
117 ) {
118 nondeterminism
119 .digests
120 .append(&mut self.extract_nondeterministic_digests(proof, claim));
121 }
122
123 fn extract_nondeterministic_digests(&self, proof: &Proof, claim: &Claim) -> Vec<Digest> {
124 const NUM_DEEP_CODEWORD_COMPONENTS: usize = 4;
125
126 fn extract_paths<R: BFieldCodec>(
127 indices: Vec<usize>,
128 leaf_preimages: Vec<R>,
129 tree_height: u32,
130 authentication_structure: Vec<Digest>,
131 ) -> Vec<Vec<Digest>> {
132 let indexed_leafs = indices
133 .into_iter()
134 .zip(leaf_preimages.iter().map(Tip5::hash))
135 .collect();
136 MerkleTreeInclusionProof {
137 tree_height,
138 indexed_leafs,
139 authentication_structure,
140 }
141 .into_authentication_paths()
142 .unwrap()
143 }
144
145 let mut proof_stream = ProofStream::try_from(proof).unwrap();
149 proof_stream.alter_fiat_shamir_state_with(claim);
150 let log2_padded_height = proof_stream
151 .dequeue()
152 .unwrap()
153 .try_into_log2_padded_height()
154 .unwrap();
155
156 let _main_table_root = proof_stream
158 .dequeue()
159 .unwrap()
160 .try_into_merkle_root()
161 .unwrap();
162
163 let _challenges = proof_stream.sample_scalars(Challenges::SAMPLE_COUNT);
165
166 let _aux_mt_root = proof_stream
168 .dequeue()
169 .unwrap()
170 .try_into_merkle_root()
171 .unwrap();
172
173 proof_stream.sample_scalars(MasterAuxTable::NUM_CONSTRAINTS);
175
176 let _quotient_root = proof_stream
178 .dequeue()
179 .unwrap()
180 .try_into_merkle_root()
181 .unwrap();
182
183 let _out_of_domain_point_curr_row = proof_stream.sample_scalars(1);
185
186 proof_stream
188 .dequeue()
189 .unwrap()
190 .try_into_out_of_domain_main_row()
191 .unwrap();
192 proof_stream
193 .dequeue()
194 .unwrap()
195 .try_into_out_of_domain_aux_row()
196 .unwrap();
197 proof_stream
198 .dequeue()
199 .unwrap()
200 .try_into_out_of_domain_main_row()
201 .unwrap();
202 proof_stream
203 .dequeue()
204 .unwrap()
205 .try_into_out_of_domain_aux_row()
206 .unwrap();
207 proof_stream
208 .dequeue()
209 .unwrap()
210 .try_into_out_of_domain_quot_segments()
211 .unwrap();
212 proof_stream
213 .dequeue()
214 .unwrap()
215 .try_into_out_of_domain_quot_segments()
216 .unwrap();
217
218 proof_stream.sample_scalars(
220 MasterMainTable::NUM_COLUMNS
221 + MasterAuxTable::NUM_COLUMNS
222 + NUM_RANDOMIZED_QUOTIENT_SEGMENTS
223 + NUM_DEEP_CODEWORD_COMPONENTS,
224 );
225
226 let padded_height = 1 << log2_padded_height;
228 let fri = self.stark.fri(padded_height).unwrap();
229 let fri_proof_stream = proof_stream.clone();
230 let fri_verify_result = fri.verify(&mut proof_stream).unwrap();
231 let indices = fri_verify_result.iter().map(|(i, _)| *i).collect_vec();
232 let tree_height = fri.domain.len().ilog2();
233 let fri_digests =
234 FriVerify::from(fri).extract_digests_required_for_proving(&fri_proof_stream);
235
236 let main_table_rows = proof_stream
238 .dequeue()
239 .unwrap()
240 .try_into_master_main_table_rows()
241 .unwrap();
242 let main_authentication_structure = proof_stream
243 .dequeue()
244 .unwrap()
245 .try_into_authentication_structure()
246 .unwrap();
247 let main_tree_auth_paths = extract_paths(
248 indices.clone(),
249 main_table_rows,
250 tree_height,
251 main_authentication_structure,
252 );
253
254 let aux_table_rows = proof_stream
256 .dequeue()
257 .unwrap()
258 .try_into_master_aux_table_rows()
259 .unwrap();
260 let aux_authentication_structure = proof_stream
261 .dequeue()
262 .unwrap()
263 .try_into_authentication_structure()
264 .unwrap();
265 let aux_tree_auth_paths = extract_paths(
266 indices.clone(),
267 aux_table_rows,
268 tree_height,
269 aux_authentication_structure,
270 );
271
272 let quot_table_rows = proof_stream
274 .dequeue()
275 .unwrap()
276 .try_into_quot_segments_elements()
277 .unwrap();
278 let quot_authentication_structure = proof_stream
279 .dequeue()
280 .unwrap()
281 .try_into_authentication_structure()
282 .unwrap();
283 let quot_tree_auth_paths = extract_paths(
284 indices,
285 quot_table_rows,
286 tree_height,
287 quot_authentication_structure,
288 );
289
290 let stark_digests = [
291 main_tree_auth_paths,
292 aux_tree_auth_paths,
293 quot_tree_auth_paths,
294 ]
295 .concat()
296 .concat();
297
298 [fri_digests, stark_digests].concat()
299 }
300}
301
302impl BasicSnippet for StarkVerify {
303 fn parameters(&self) -> Vec<(DataType, String)> {
304 let claim_type = DataType::StructRef(claim_type());
305 vec![
306 (claim_type, "claim".to_string()),
307 (DataType::VoidPointer, "*proof".to_string()),
308 ]
309 }
310
311 fn return_values(&self) -> Vec<(DataType, String)> {
312 vec![]
313 }
314
315 fn entrypoint(&self) -> String {
316 let memory_layout_category = self.memory_layout.label_friendly_name();
317 format!("tasmlib_verifier_stark_verify_{memory_layout_category}")
318 }
319
320 fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
321 const NUM_DEEP_CODEWORD_COMPONENTS: usize = 4;
322 const NUM_OOD_ROWS_WO_QUOTIENT: u32 = 4;
323
324 fn fri_snippet() -> FriSnippet {
325 FriSnippet {
326 #[cfg(test)]
327 test_instance: FriVerify::dummy(),
328 }
329 }
330
331 let entrypoint = self.entrypoint();
332
333 let proof_to_vm_proof_iter = library.import(Box::new(New));
334 let drop_vm_proof_iter = library.import(Box::new(Drop));
335
336 let ood_curr_row_main_and_aux_value_pointer_alloc =
337 library.kmalloc(EXTENSION_DEGREE.try_into().unwrap());
338 let ood_next_row_main_and_aux_value_pointer_alloc =
339 library.kmalloc(EXTENSION_DEGREE.try_into().unwrap());
340 let ood_curr_row_quotient_segment_value_for_p_pointer_alloc =
341 library.kmalloc(EXTENSION_DEGREE.try_into().unwrap());
342 let ood_curr_row_quotient_segment_value_for_r_pointer_alloc =
343 library.kmalloc(EXTENSION_DEGREE.try_into().unwrap());
344
345 let ood_curr_row_quot_segments_for_p_pointer_alloc = library.kmalloc(1);
346 let ood_curr_row_quot_segments_for_r_pointer_alloc = library.kmalloc(1);
347
348 let instantiate_fiat_shamir_with_claim =
349 library.import(Box::new(InstantiateFiatShamirWithClaim));
350 let next_as_log_2_padded_height = library.import(Box::new(DequeueNextAs {
351 proof_item: ProofItemVariant::Log2PaddedHeight,
352 }));
353 let next_as_merkleroot = library.import(Box::new(DequeueNextAs {
354 proof_item: ProofItemVariant::MerkleRoot,
355 }));
356 let next_as_outofdomainmainrow = library.import(Box::new(DequeueNextAs {
357 proof_item: ProofItemVariant::OutOfDomainMainRow,
358 }));
359 let next_as_outofdomainauxrow = library.import(Box::new(DequeueNextAs {
360 proof_item: ProofItemVariant::OutOfDomainAuxRow,
361 }));
362 let next_as_outofdomainquotientsegments = library.import(Box::new(DequeueNextAs {
363 proof_item: ProofItemVariant::OutOfDomainQuotientSegments,
364 }));
365 let next_as_maintablerows = library.import(Box::new(DequeueNextAs {
366 proof_item: ProofItemVariant::MasterMainTableRows,
367 }));
368 let next_as_authentication_path = library.import(Box::new(DequeueNextAs {
369 proof_item: ProofItemVariant::AuthenticationStructure,
370 }));
371 let next_as_auxtablerows = library.import(Box::new(DequeueNextAs {
372 proof_item: ProofItemVariant::MasterAuxTableRows,
373 }));
374 let next_as_quotient_segment_elements = library.import(Box::new(DequeueNextAs {
375 proof_item: ProofItemVariant::QuotientSegmentsElements,
376 }));
377 let derive_fri_parameters =
378 library.import(Box::new(fri::derive_from_stark::DeriveFriFromStark {
379 stark: self.stark,
380 }));
381 let num_collinearity_checks_field = field!(FriVerify::num_collinearity_checks);
382 let domain_length_field = field!(FriVerify::domain_length);
383 let domain_offset_field = field!(FriVerify::domain_offset);
384 let domain_generator_field = field!(FriVerify::domain_generator);
385
386 let fri_verify = library.import(Box::new(fri_snippet()));
387
388 let get_challenges = library.import(Box::new(
389 challenges::new_generic_dyn_claim::NewGenericDynClaim::tvm_challenges(
390 self.memory_layout.challenges_pointer(),
391 ),
392 ));
393 let sample_quotient_codeword_weights =
394 library.import(Box::new(SampleScalarsStaticLengthDynMalloc {
395 num_elements: MasterAuxTable::NUM_CONSTRAINTS,
396 }));
397 let domain_generator = library.import(Box::new(PrimitiveRootOfUnity));
398 let sample_scalar_one = library.import(Box::new(SampleScalarOne));
399 let calculate_out_of_domain_points = library.import(Box::new(OutOfDomainPoints));
400 let divide_out_zerofiers = library.import(Box::new(DivideOutZerofiers));
401 let inner_product_quotient_summands = library.import(Box::new(InnerProductOfXfes {
402 length: MasterAuxTable::NUM_CONSTRAINTS,
403 }));
404 let horner_evaluation_of_ood_curr_row_quot_segments =
405 library.import(Box::new(HornerEvaluation {
406 num_coefficients: NUM_QUOTIENT_SEGMENTS,
407 }));
408 let sample_beqd_weights = library.import(Box::new(SampleScalarsStaticLengthDynMalloc {
409 num_elements: MasterMainTable::NUM_COLUMNS
410 + MasterAuxTable::NUM_COLUMNS
411 + NUM_RANDOMIZED_QUOTIENT_SEGMENTS
412 + NUM_DEEP_CODEWORD_COMPONENTS,
413 }));
414 let verify_main_table_rows = library.import(Box::new(VerifyTableRows {
415 column_type: ColumnType::Main,
416 }));
417 let verify_aux_table_rows = library.import(Box::new(VerifyTableRows {
418 column_type: ColumnType::Aux,
419 }));
420 let verify_quotient_segments = library.import(Box::new(VerifyTableRows {
421 column_type: ColumnType::Quotient,
422 }));
423 let inner_product_three_rows_with_weights_bfe_main = library.import(Box::new(
424 InnerProductOfThreeRowsWithWeights::triton_vm_parameters(MainElementType::Bfe),
425 ));
426 let inner_product_three_rows_with_weights_xfe_main = library.import(Box::new(
427 InnerProductOfThreeRowsWithWeights::triton_vm_parameters(MainElementType::Xfe),
428 ));
429 let inner_product_4_xfes = library.import(Box::new(InnerProductOfXfes { length: 4 }));
430 let quotient_segment_codeword_weights_from_be_weights = triton_asm!(
431 addi {(MasterMainTable::NUM_COLUMNS + MasterAuxTable::NUM_COLUMNS) * EXTENSION_DEGREE}
434 );
436 let deep_codeword_weights_read_address = |n: usize| {
437 assert!(n < NUM_DEEP_CODEWORD_COMPONENTS);
438 triton_asm!(
439 addi {(MasterMainTable::NUM_COLUMNS + MasterAuxTable::NUM_COLUMNS + NUM_RANDOMIZED_QUOTIENT_SEGMENTS + n) * EXTENSION_DEGREE + {EXTENSION_DEGREE - 1}}
442 )
444 };
445
446 let dequeue_four_ood_rows = triton_asm! {
447 dup 0
449 call {next_as_outofdomainmainrow}
450 hint out_of_domain_curr_main_row: Pointer = stack[0]
451
452 dup 1
453 call {next_as_outofdomainauxrow}
454 hint out_of_domain_curr_aux_row: Pointer = stack[0]
455
456 dup 2
457 call {next_as_outofdomainmainrow}
458 hint out_of_domain_next_main_row: Pointer = stack[0]
459
460 dup 3
461 call {next_as_outofdomainauxrow}
462 hint out_of_domain_next_aux_row: Pointer = stack[0]
463 };
465
466 let ood_pointers_alloc = library.kmalloc(NUM_OOD_ROWS_WO_QUOTIENT);
471 let evaluate_air_and_store_ood_pointers = match self.memory_layout {
472 MemoryLayout::Static(static_layout) => {
473 let static_eval =
474 library.import(Box::new(AirConstraintEvaluation::new_static(static_layout)));
475 triton_asm! {
476 push {ood_pointers_alloc.write_address()}
477 write_mem {ood_pointers_alloc.num_words()}
478
479 pop 2
480 call {static_eval}
483 }
485 }
486 MemoryLayout::Dynamic(dynamic_layout) => {
487 let dynamic_eval = library.import(Box::new(AirConstraintEvaluation::new_dynamic(
488 dynamic_layout,
489 )));
490 triton_asm! {
491 dup 3
493 dup 3
494 dup 3
495 dup 3
496 push {ood_pointers_alloc.write_address()}
497 write_mem {ood_pointers_alloc.num_words()}
498 pop 1
499 call {dynamic_eval}
502 pick 1 pop 1
505 }
507 }
508 };
509
510 let put_ood_row_pointers_back_on_stack = triton_asm! {
511 push {ood_pointers_alloc.read_address()}
513 read_mem {ood_pointers_alloc.num_words()}
514 pop 1
515
516 };
518
519 let challenges_ptr = self.memory_layout.challenges_pointer();
520
521 let assert_top_two_xfes_eq = triton_asm!(
522 pick 3
524 eq
525 assert error_id 230
526
527 pick 2
529 eq
530 assert error_id 231
531
532 eq
534 assert error_id 232
535
536 );
538
539 let main_loop_label = format!("{entrypoint}_main_loop");
540 let main_loop_body = triton_asm!(
541 pick 2
545 read_mem 1
546 place 3
547 dup 8
550 pow
551 dup 7
552 mul
553 push -1
554 mul
555 hint neg_fri_domain_point = stack[0]
556 dup 6
560 dup 6
561 dup 4
562 call {inner_product_three_rows_with_weights_bfe_main} hint main_and_aux_opened_row_element: Xfe = stack[0..3]
564 pick 9
568 addi {-bfe!(EXTENSION_DEGREE * MasterAuxTable::NUM_COLUMNS)}
569 place 9
570 pick 8
571 addi {-bfe!(MasterMainTable::NUM_COLUMNS)}
572 place 8
573 push -1
576 xb_mul
577 hint neg_main_and_aux_opened_row_element: Xfe = stack[0..3]
578
579 dup 4
581 {&OutOfDomainPoints::read_ood_point(OodPoint::CurrentRowPowNumSegments)}
582 dup 6
585 add
586 x_invert
589 dup 8
592 {"ient_segment_codeword_weights_from_be_weights}
593 dup 11
594 call {inner_product_4_xfes}
595 push -1
598 xb_mul
599 push {ood_curr_row_quotient_segment_value_for_p_pointer_alloc.read_address()}
602 read_mem {EXTENSION_DEGREE}
603 pop 1
604 xx_add
607 xx_mul
608 hint quot_curr_row_deep_value_for_p: XFieldElement = stack[0..3]
609 dup 8
615 {&deep_codeword_weights_read_address(2)}
616 read_mem {EXTENSION_DEGREE}
617 pop 1
618 xx_mul
619 dup 7
625 {&OutOfDomainPoints::read_ood_point(OodPoint::CurrentRowTimesZetaPowNumSegments)}
626 dup 9
629 add
630 x_invert
631 dup 11
634 {"ient_segment_codeword_weights_from_be_weights}
635 addi {EXTENSION_DEGREE}
636 dup 14
639 addi {EXTENSION_DEGREE}
640 pick 15
644 addi {-bfe!(NUM_RANDOMIZED_QUOTIENT_SEGMENTS * EXTENSION_DEGREE)}
645 place 15
646 call {inner_product_4_xfes}
649 push -1
652 xb_mul
653 push {ood_curr_row_quotient_segment_value_for_r_pointer_alloc.read_address()}
656 read_mem {EXTENSION_DEGREE}
657 pop 1
658 xx_add
661 xx_mul
662 hint quot_curr_row_deep_value_for_r: XFieldElement = stack[0..3]
663 dup 11
668 {&deep_codeword_weights_read_address(3)}
669 read_mem {EXTENSION_DEGREE}
670 pop 1
671 xx_mul
672 xx_add
675 dup 7
678 {&OutOfDomainPoints::read_ood_point(OodPoint::CurrentRow)}
679 dup 9
682 add
683 x_invert
684 push {ood_curr_row_main_and_aux_value_pointer_alloc.read_address()}
687 read_mem {EXTENSION_DEGREE}
688 pop 1
689 dup 11
692 dup 11
693 dup 11
694 xx_add
695 xx_mul
698 dup 11
701 {&deep_codeword_weights_read_address(0)}
702 read_mem {EXTENSION_DEGREE} pop 1
704 xx_mul
705 xx_add
709 hint dv2_plus_dv3_plus_dv0: XFieldElement = stack[0..3]
711
712 pick 5
713 pick 5
714 pick 5
715 push {ood_next_row_main_and_aux_value_pointer_alloc.read_address()}
718 read_mem {EXTENSION_DEGREE}
719 pop 1
720 xx_add
721 dup 7
724 {&OutOfDomainPoints::read_ood_point(OodPoint::NextRow)}
725 dup 9
728 add
729 x_invert
732 xx_mul
733 dup 8
736 {&deep_codeword_weights_read_address(1)}
737 read_mem {EXTENSION_DEGREE} pop 1
739 xx_mul
740 xx_add
744 hint deep_value: XFieldElement = stack[0..3]
745 pick 3
749 pop 1
750 pick 5
753 read_mem {EXTENSION_DEGREE}
754 place 8
755 {&assert_top_two_xfes_eq}
758
759 );
761 let main_loop = triton_asm!(
762 {main_loop_label}:
765 dup 8
767 push 0
768 eq
769 skiz
770 return
771
772 {&main_loop_body}
773
774 pick 8
776 addi -1
777 place 8
778 recurse
779 );
780
781 triton_asm!(
782 {entrypoint}:
783 sponge_init
784 call {proof_to_vm_proof_iter}
787 hint proof_iter = stack[0]
788
789 dup 1
794 call {instantiate_fiat_shamir_with_claim}
795 dup 0
800 call {next_as_log_2_padded_height}
801 read_mem 1
804 pop 1
805 push 32
809 dup 1
810 lt
811 assert error_id {Self::LOG2_PADDED_HEIGHT_TOO_LARGE}
814 push 8
817 dup 1
818 lt
819 push 0
820 eq
821 assert error_id {Self::LOG2_PADDED_HEIGHT_TOO_SMALL}
824
825
826 push 2
827 pow
828 hint padded_height = stack[0]
829 dup 0
832 call {derive_fri_parameters}
833 hint fri = stack[0]
834 dup 0
845 {&domain_length_field}
846 read_mem 1
847 pop 1
848 push {(bfe!(2) * bfe!(self.stark.fri_expansion_factor as u64)).inverse()}
851 mul
852 hint trace_domain_len = stack[0]
853 place 2
856 pick 1
857 pop 1
858 dup 2
862 call {next_as_merkleroot}
863 hint b_mr = stack[0]
864 swap 4
867 call {get_challenges}
870 push {challenges_ptr}
874 eq
875 assert error_id 233
876 dup 2
879 call {next_as_merkleroot}
880 hint e_mr = stack[0]
881 call {sample_quotient_codeword_weights}
884 hint quot_codeword_weights = stack[0]
886
887 dup 4
888 call {next_as_merkleroot}
889 hint quot_mr = stack[0]
890 push 0
895 dup 5
896 call {domain_generator}
897 hint trace_domain_generator = stack[0]
898 dup 0
901 call {sample_scalar_one}
904 call {calculate_out_of_domain_points}
907 hint out_of_domain_points = stack[0]
908 push 2
913 add
914 read_mem {EXTENSION_DEGREE}
915 push 1
916 add
917 swap 9
920 dup 10
923 {&dequeue_four_ood_rows}
924 {&evaluate_air_and_store_ood_pointers}
927 swap 5
930 call {divide_out_zerofiers}
933 {&put_ood_row_pointers_back_on_stack}
936 dup 10
939 call {next_as_outofdomainquotientsegments}
940 hint out_of_domain_quot_segments_for_p: Pointer = stack[0]
941 dup 0
944 push {ood_curr_row_quot_segments_for_p_pointer_alloc.write_address()}
945 write_mem {ood_curr_row_quot_segments_for_p_pointer_alloc.num_words()}
946 pop 1
947
948 dup 11
949 call {next_as_outofdomainquotientsegments}
950 hint out_of_domain_quot_segments_for_r: Pointer = stack[0]
951 dup 0
954 push {ood_curr_row_quot_segments_for_r_pointer_alloc.write_address()}
955 write_mem {ood_curr_row_quot_segments_for_r_pointer_alloc.num_words()}
956 pop 1
957
958
959 dup 11
962 {&OutOfDomainPoints::read_ood_point(OodPoint::CurrentRow)}
963 push {Stark::ZETA}
966 xb_mul
967 call {horner_evaluation_of_ood_curr_row_quot_segments}
970 pick 3
973 dup 13
976 {&OutOfDomainPoints::read_ood_point(OodPoint::CurrentRow)}
977 call {horner_evaluation_of_ood_curr_row_quot_segments}
980 xx_add
983 pick 4 place 9
988 pick 3 place 6
989 pick 8 pick 6
990 call {inner_product_quotient_summands}
993 {&assert_top_two_xfes_eq}
997 call {sample_beqd_weights}
1001 hint beqd_weights = stack[0]
1002 swap 10
1005 dup 9
1011 dup 8
1012 call {fri_verify}
1013 hint fri_revealed = stack[0]
1014 dup 10
1019 call {next_as_maintablerows}
1020 dup 9
1024 {&num_collinearity_checks_field}
1025 read_mem 1
1026 pop 1
1027 dup 10
1030 {&domain_length_field}
1031 read_mem 1
1032 pop 1
1033 log_2_floor
1036 pick 4
1039 dup 4
1042 dup 4
1045 call {verify_main_table_rows}
1048 dup 10
1053 call {next_as_authentication_path}
1054 pop 1
1055 swap 7
1058 dup 10
1063 call {next_as_auxtablerows}
1064 pick 1
1067 dup 9
1070 {&num_collinearity_checks_field}
1071 read_mem 1
1072 pop 1
1073 dup 10
1074 {&domain_length_field}
1075 read_mem 1
1076 pop 1
1077 log_2_floor
1078 pick 2
1081 dup 4
1082 dup 4
1083 call {verify_aux_table_rows}
1086 dup 10
1091 call {next_as_authentication_path}
1092 pop 1
1093
1094 swap 5
1095 dup 10
1100 call {next_as_quotient_segment_elements}
1101 swap 1
1102 dup 9
1103 {&num_collinearity_checks_field}
1104 read_mem 1
1105 pop 1
1106 dup 10
1107 {&domain_length_field}
1108 read_mem 1
1109 pop 1
1110 log_2_floor
1111 pick 2
1114 dup 4
1117 dup 4
1118 call {verify_quotient_segments}
1121 dup 8
1126 {&num_collinearity_checks_field}
1127 read_mem 1
1128 pop 1
1129 hint num_combination_codeword_checks = stack[0]
1130 dup 2
1134 read_mem 1
1135 pop 1
1136 dup 1
1137 eq
1138 assert error_id 234
1139 dup 8
1143 read_mem 1
1144 pop 1
1145 dup 1
1146 eq
1147 assert error_id 235
1148
1149 dup 6
1151 read_mem 1
1152 pop 1
1153 dup 1
1154 eq
1155 assert error_id 236
1156
1157 dup 1
1159 read_mem 1
1160 pop 1
1161 dup 1
1162 eq
1163 assert error_id 237
1164 swap 12
1169 swap 11
1170 dup 0
1171 call {next_as_authentication_path}
1172 pop 1
1173
1174 call {drop_vm_proof_iter}
1175 dup 10
1181 swap 2
1182 swap 4
1185 swap 1
1186 swap 3
1187 swap 2
1188 call {inner_product_three_rows_with_weights_xfe_main} hint out_of_domain_curr_row_main_and_aux_value: XFieldElement = stack[0..3]
1192 push {ood_curr_row_main_and_aux_value_pointer_alloc.write_address()}
1195 write_mem {ood_curr_row_main_and_aux_value_pointer_alloc.num_words()}
1196 pop 1
1197 swap 2
1202 swap 1
1203 swap 4
1204 dup 8
1205 call {inner_product_three_rows_with_weights_xfe_main} hint out_of_domain_next_row_main_and_aux_value: XFieldElement = stack[0..3]
1209 push {ood_next_row_main_and_aux_value_pointer_alloc.write_address()}
1212 write_mem {ood_next_row_main_and_aux_value_pointer_alloc.num_words()}
1213 pop 1
1214 dup 6
1218 {"ient_segment_codeword_weights_from_be_weights}
1219 push {ood_curr_row_quot_segments_for_p_pointer_alloc.read_address()}
1222 read_mem {ood_curr_row_quot_segments_for_p_pointer_alloc.num_words()}
1223 pop 1
1224 call {inner_product_4_xfes}
1227 hint out_of_domain_curr_row_quotient_segment_value_for_p: XFieldElement = stack[0..3]
1228 push {ood_curr_row_quotient_segment_value_for_p_pointer_alloc.write_address()}
1231 write_mem {ood_curr_row_quotient_segment_value_for_p_pointer_alloc.num_words()}
1232 pop 1
1233 dup 6
1237 {"ient_segment_codeword_weights_from_be_weights}
1238 addi {EXTENSION_DEGREE}
1239 push {ood_curr_row_quot_segments_for_r_pointer_alloc.read_address()}
1242 read_mem {ood_curr_row_quot_segments_for_r_pointer_alloc.num_words()}
1243 pop 1
1244 call {inner_product_4_xfes}
1247 hint out_of_domain_curr_row_quotient_segment_value_for_r: XFieldElement = stack[0..3]
1248 push {ood_curr_row_quotient_segment_value_for_r_pointer_alloc.write_address()}
1251 write_mem {ood_curr_row_quotient_segment_value_for_r_pointer_alloc.num_words()}
1252 pop 1
1253 swap 4
1257 dup 0
1258 {&domain_offset_field}
1259 read_mem 1
1260 pop 1
1261 hint fri_domain_offset = stack[0]
1262 swap 1
1265 {&domain_generator_field}
1266 read_mem 1
1267 pop 1
1268 hint fri_domain_gen = stack[0]
1269 pick 3
1276 dup 8
1277 push {EXTENSION_DEGREE + 1} mul
1279 add
1280 hint fri_revealed_elem = stack[0]
1281 place 3
1282
1283 pick 4
1285 addi 1
1286 dup 8
1287 addi -1
1288 push {MasterMainTable::NUM_COLUMNS} mul
1290 add
1291 hint main_table_row = stack[0]
1292 place 4
1293
1294 pick 2
1296 addi 1
1297 dup 8
1298 addi -1
1299 push {MasterAuxTable::NUM_COLUMNS * EXTENSION_DEGREE} mul
1301 add
1302 hint aux_table_row = stack[0]
1303 place 2
1304
1305 pick 5
1307 addi 1
1308 dup 8
1309 addi -1
1310 push {NUM_RANDOMIZED_QUOTIENT_SEGMENTS * EXTENSION_DEGREE} mul
1312 add
1313 hint quotient_segment_elem = stack[0]
1314 place 5
1315
1316 place 7
1320 place 6
1321 place 5
1322 place 4
1323 place 4
1324 place 3
1325 call {main_loop_label}
1328 pop 5 pop 4
1332
1333 return
1334
1335 {&main_loop}
1336 )
1337 }
1338}
1339
1340#[cfg(test)]
1341pub mod tests {
1342 use std::collections::HashMap;
1343
1344 use num_traits::ConstZero;
1345 use tasm_object_derive::TasmObject;
1346 use triton_vm::proof_item::ProofItem;
1347
1348 use super::*;
1349 use crate::execute_test;
1350 use crate::maybe_write_debuggable_vm_state_to_disk;
1351 use crate::memory::FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS;
1352 use crate::test_helpers::maybe_write_tvm_output_to_disk;
1353 use crate::test_prelude::*;
1354 use crate::verifier::claim::shared::insert_claim_into_static_memory;
1355 use crate::verifier::master_table::air_constraint_evaluation::an_integral_but_profane_dynamic_memory_layout;
1356
1357 #[ignore = "Used for debugging when comparing two versions of the verifier"]
1358 #[macro_rules_attr::apply(test)]
1359 fn verify_from_stored_proof_output() {
1360 use std::fs::File;
1361 let stark = File::open("stark.json").expect("stark file should open read only");
1362 let stark: Stark = serde_json::from_reader(stark).unwrap();
1363 let claim = File::open("claim.json").expect("claim file should open read only");
1364 let claim_for_proof: Claim = serde_json::from_reader(claim).unwrap();
1365 let proof = File::open("proof.json").expect("proof file should open read only");
1366 let proof: Proof = serde_json::from_reader(proof).unwrap();
1367
1368 let snippet = StarkVerify {
1369 stark,
1370 memory_layout: MemoryLayout::conventional_dynamic(),
1371 };
1372 let mut nondeterminism = NonDeterminism::new(vec![]);
1373 snippet.update_nondeterminism(&mut nondeterminism, &proof, &claim_for_proof);
1374
1375 let (claim_pointer, claim_size) =
1376 insert_claim_into_static_memory(&mut nondeterminism.ram, &claim_for_proof);
1377
1378 let default_proof_pointer = BFieldElement::ZERO;
1379
1380 let mut init_stack = [
1381 snippet.init_stack_for_isolated_run(),
1382 vec![claim_pointer, default_proof_pointer],
1383 ]
1384 .concat();
1385 let code = snippet.link_for_isolated_run_populated_static_memory(claim_size);
1386 let _final_tasm_state = execute_test(
1387 &code,
1388 &mut init_stack,
1389 snippet.stack_diff(),
1390 vec![],
1391 nondeterminism,
1392 None,
1393 );
1394 }
1395
1396 fn vm_state_for_log2_padded_height_proof_element(log2_padded_height: u32) -> VMState {
1397 let mut proof_stream = ProofStream::new();
1398 proof_stream.enqueue(ProofItem::Log2PaddedHeight(log2_padded_height));
1399 let proof: Proof = proof_stream.into();
1400
1401 let mut nondeterminism = NonDeterminism::new(vec![]);
1402 encode_to_memory(
1403 &mut nondeterminism.ram,
1404 FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS,
1405 &proof,
1406 );
1407 let (claim_pointer, claim_size) = insert_claim_into_static_memory(
1408 &mut nondeterminism.ram,
1409 &Claim::new(Digest::default()),
1410 );
1411
1412 let snippet = StarkVerify {
1413 stark: Stark::default(),
1414 memory_layout: MemoryLayout::conventional_static(),
1415 };
1416 let init_stack = [
1417 snippet.init_stack_for_isolated_run(),
1418 vec![
1419 claim_pointer,
1420 FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS,
1421 ],
1422 ]
1423 .concat();
1424
1425 let program =
1426 Program::new(&snippet.link_for_isolated_run_populated_static_memory(claim_size));
1427 let mut vm_state = VMState::new(program, [].into(), nondeterminism.clone());
1428 vm_state.op_stack.stack = init_stack.clone();
1429
1430 vm_state
1431 }
1432
1433 #[macro_rules_attr::apply(test)]
1434 fn fail_on_too_big_log2_padded_height() {
1435 for too_big_padded_height in [32, 33, 40, 41, 999] {
1436 let mut vm_state = vm_state_for_log2_padded_height_proof_element(too_big_padded_height);
1437 let error = vm_state.run().unwrap_err();
1438 match error {
1439 InstructionError::AssertionFailed(assertion_error) => {
1440 assert_eq!(
1441 StarkVerify::LOG2_PADDED_HEIGHT_TOO_LARGE,
1442 assertion_error.id.unwrap()
1443 );
1444 }
1445 _ => panic!(),
1446 }
1447 }
1448 }
1449
1450 #[macro_rules_attr::apply(test)]
1451 fn fail_on_too_small_log2_padded_height() {
1452 for too_small_padded_height in 0..8 {
1453 let mut vm_state =
1454 vm_state_for_log2_padded_height_proof_element(too_small_padded_height);
1455 let error = vm_state.run().unwrap_err();
1456 match error {
1457 InstructionError::AssertionFailed(assertion_error) => {
1458 assert_eq!(
1459 StarkVerify::LOG2_PADDED_HEIGHT_TOO_SMALL,
1460 assertion_error.id.unwrap()
1461 );
1462 }
1463 _ => panic!(),
1464 }
1465 }
1466 }
1467
1468 fn test_verify_and_report_basic_features(
1471 inner_nondeterminism: NonDeterminism,
1472 inner_program: Program,
1473 inner_public_input: &[BFieldElement],
1474 stark: Stark,
1475 layout: MemoryLayout,
1476 ) -> (usize, usize) {
1477 let (mut non_determinism, claim_for_proof) = prove_and_get_non_determinism_and_claim(
1478 inner_program.clone(),
1479 inner_public_input,
1480 inner_nondeterminism.clone(),
1481 &stark,
1482 );
1483
1484 let (claim_pointer, claim_size) =
1485 insert_claim_into_static_memory(&mut non_determinism.ram, &claim_for_proof);
1486
1487 let default_proof_pointer = bfe!(0);
1488
1489 let snippet = StarkVerify {
1490 stark,
1491 memory_layout: layout,
1492 };
1493 let mut init_stack = [
1494 snippet.init_stack_for_isolated_run(),
1495 vec![claim_pointer, default_proof_pointer],
1496 ]
1497 .concat();
1498 let code = snippet.link_for_isolated_run_populated_static_memory(claim_size);
1499
1500 let program = Program::new(&code);
1501 let mut vm_state = VMState::new(program, [].into(), non_determinism.clone());
1502 vm_state.op_stack.stack = init_stack.clone();
1503 maybe_write_debuggable_vm_state_to_disk(&vm_state);
1504
1505 let final_tasm_state = execute_test(
1506 &code,
1507 &mut init_stack,
1508 snippet.stack_diff(),
1509 vec![],
1510 non_determinism,
1511 None,
1512 )
1513 .unwrap();
1514
1515 let (aet, _public_output) = VM::trace_execution(
1516 inner_program,
1517 (&claim_for_proof.input).into(),
1518 inner_nondeterminism,
1519 )
1520 .unwrap();
1521 let inner_padded_height = aet.padded_height();
1522
1523 (final_tasm_state.cycle_count as usize, inner_padded_height)
1524 }
1525
1526 #[macro_rules_attr::apply(test)]
1527 fn different_fri_expansion_factors() {
1528 const FACTORIAL_ARGUMENT: u32 = 3;
1529
1530 for log2_of_fri_expansion_factor in 2..=5 {
1531 println!("log2_of_fri_expansion_factor: {log2_of_fri_expansion_factor}");
1532 let factorial_program = factorial_program_with_io();
1533 let stark = Stark::new(160, log2_of_fri_expansion_factor);
1534 let (cycle_count, inner_padded_height) = test_verify_and_report_basic_features(
1535 NonDeterminism::default(),
1536 factorial_program,
1537 &[FACTORIAL_ARGUMENT.into()],
1538 stark,
1539 MemoryLayout::conventional_static(),
1540 );
1541 println!(
1542 "TASM-verifier of factorial({FACTORIAL_ARGUMENT}):\n
1543 Fri expansion factor: {}\n
1544 clock cycle count: {}.\n
1545 Inner padded height was: {}",
1546 1 << log2_of_fri_expansion_factor,
1547 cycle_count,
1548 inner_padded_height,
1549 );
1550 }
1551 }
1552
1553 #[macro_rules_attr::apply(test)]
1554 fn verify_shortest_possible_execution() {
1555 let stark = Stark::default();
1556
1557 for memory_layout in [
1558 MemoryLayout::conventional_static(),
1559 MemoryLayout::conventional_dynamic(),
1560 ] {
1561 let program = triton_program!(halt);
1562 let (_, inner_padded_height) = test_verify_and_report_basic_features(
1563 NonDeterminism::default(),
1564 program,
1565 &[],
1566 stark,
1567 memory_layout,
1568 );
1569 assert_eq!(
1570 256, inner_padded_height,
1571 "This version of Triton VM has minimum padded height of 256"
1572 )
1573 }
1574 }
1575
1576 fn verify_tvm_proof_factorial_program_basic_properties(mem_layout: MemoryLayout) {
1577 const FACTORIAL_ARGUMENT: u32 = 3;
1578
1579 let factorial_program = factorial_program_with_io();
1580 let stark = Stark::default();
1581 let (cycle_count, inner_padded_height) = test_verify_and_report_basic_features(
1582 NonDeterminism::default(),
1583 factorial_program,
1584 &[FACTORIAL_ARGUMENT.into()],
1585 stark,
1586 mem_layout,
1587 );
1588
1589 println!(
1590 "TASM-verifier of factorial({FACTORIAL_ARGUMENT}):\n
1591 clock cycle count: {cycle_count}.\n
1592 Inner padded height was: {inner_padded_height}",
1593 );
1594 }
1595
1596 #[macro_rules_attr::apply(test)]
1597 fn verify_tvm_proof_factorial_program_conventional_static_memlayout() {
1598 verify_tvm_proof_factorial_program_basic_properties(MemoryLayout::conventional_static());
1599 }
1600
1601 #[macro_rules_attr::apply(test)]
1602 fn verify_tvm_proof_factorial_program_conventional_dynamic_memlayout() {
1603 verify_tvm_proof_factorial_program_basic_properties(MemoryLayout::conventional_dynamic());
1604 }
1605
1606 #[macro_rules_attr::apply(test)]
1607 fn verify_tvm_proof_factorial_program_profane_dynamic_memlayout() {
1608 verify_tvm_proof_factorial_program_basic_properties(MemoryLayout::Dynamic(
1609 an_integral_but_profane_dynamic_memory_layout(),
1610 ));
1611 }
1612
1613 pub(super) fn factorial_program_with_io() -> Program {
1614 triton_program!(
1615 read_io 1
1616 push 1 call factorial write_io 1
1619 halt
1620
1621 factorial: dup 1 push 0 eq skiz return dup 1 mul pick 1 addi -1 place 1 recurse
1635 )
1636 }
1637
1638 pub fn prove_and_get_non_determinism_and_claim(
1647 inner_program: Program,
1648 inner_public_input: &[BFieldElement],
1649 inner_nondeterminism: NonDeterminism,
1650 stark: &Stark,
1651 ) -> (NonDeterminism, Claim) {
1652 println!("Generating proof for non-determinism");
1653
1654 let inner_input = inner_public_input.to_vec();
1655 let claim = Claim::about_program(&inner_program).with_input(inner_input.clone());
1656 let (aet, inner_output) =
1657 VM::trace_execution(inner_program, inner_input.into(), inner_nondeterminism).unwrap();
1658 let claim = claim.with_output(inner_output);
1659
1660 triton_vm::profiler::start("inner program");
1661 let seed = [
1662 227, 232, 115, 183, 84, 194, 68, 59, 166, 60, 140, 218, 88, 117, 227, 129, 10, 121,
1663 108, 40, 65, 125, 143, 31, 155, 128, 202, 75, 218, 44, 120, 170,
1664 ];
1665 let prover = Prover::new(*stark).set_randomness_seed_which_may_break_zero_knowledge(seed);
1666 let proof = prover.prove(&claim, &aet).unwrap();
1667 let profile = triton_vm::profiler::finish();
1668 let padded_height = proof.padded_height().unwrap();
1669 let report = profile
1670 .with_cycle_count(aet.processor_trace.nrows())
1671 .with_padded_height(padded_height)
1672 .with_fri_domain_len(stark.fri(padded_height).unwrap().domain.len());
1673 println!("Done generating proof for non-determinism");
1674 println!("{report}");
1675
1676 assert!(
1677 stark.verify(&claim, &proof).is_ok(),
1678 "Proof from TVM must verify through TVM"
1679 );
1680
1681 maybe_write_tvm_output_to_disk(stark, &claim, &proof);
1682
1683 let mut nondeterminism = NonDeterminism::new(vec![]);
1684 let stark_verify = StarkVerify {
1685 stark: *stark,
1686 memory_layout: MemoryLayout::conventional_static(),
1687 };
1688
1689 let actual_num_extracted_digests = stark_verify
1691 .extract_nondeterministic_digests(&proof, &claim)
1692 .len();
1693 let expected_num_extracted_digests =
1694 stark_verify.number_of_nondeterministic_digests_consumed(&proof);
1695 assert_eq!(
1696 actual_num_extracted_digests, expected_num_extracted_digests,
1697 "Number of extracted digests must match expected value"
1698 );
1699
1700 stark_verify.update_nondeterminism(&mut nondeterminism, &proof, &claim);
1701 encode_to_memory(
1702 &mut nondeterminism.ram,
1703 FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS,
1704 &proof,
1705 );
1706
1707 (nondeterminism, claim)
1708 }
1709
1710 #[macro_rules_attr::apply(test)]
1711 fn verify_two_proofs() {
1712 #[derive(Debug, Clone, BFieldCodec, TasmObject)]
1713 struct TwoProofs {
1714 proof1: Proof,
1715 claim1: Claim,
1716 proof2: Proof,
1717 claim2: Claim,
1718 }
1719
1720 let stark = Stark::default();
1721 let stark_snippet = StarkVerify::new_with_dynamic_layout(stark);
1722
1723 let mut library = Library::new();
1724 let stark_verify = library.import(Box::new(stark_snippet));
1725 let proof1 = field!(TwoProofs::proof1);
1726 let proof2 = field!(TwoProofs::proof2);
1727 let claim1 = field!(TwoProofs::claim1);
1728 let claim2 = field!(TwoProofs::claim2);
1729 let verify_two_proofs_program = triton_asm! {
1730
1731
1732 push {FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS}
1733 dup 0
1736 {&claim1}
1737
1738 dup 1
1739 {&proof1}
1740 call {stark_verify}
1743
1744 dup 0
1745 {&claim2}
1746
1747 dup 1
1748 {&proof2}
1749 call {stark_verify}
1752
1753 halt
1754
1755 {&library.all_imports()}
1756 };
1757
1758 let inner_input_1 = bfe_vec![3];
1759 let factorial_program = factorial_program_with_io();
1760 let (aet_1, inner_output_1) = VM::trace_execution(
1761 factorial_program.clone(),
1762 inner_input_1.clone().into(),
1763 [].into(),
1764 )
1765 .unwrap();
1766 let claim_1 = Claim::about_program(&factorial_program)
1767 .with_input(inner_input_1.clone())
1768 .with_output(inner_output_1.clone());
1769 let proof_1 = stark.prove(&claim_1, &aet_1).unwrap();
1770 let padded_height_1 = proof_1.padded_height().unwrap();
1771 println!("padded_height_1: {padded_height_1}");
1772
1773 let inner_input_2 = bfe_vec![25];
1774 let (aet_2, inner_output_2) = VM::trace_execution(
1775 factorial_program.clone(),
1776 inner_input_2.clone().into(),
1777 [].into(),
1778 )
1779 .unwrap();
1780 let claim_2 = Claim::about_program(&factorial_program)
1781 .with_input(inner_input_2.clone())
1782 .with_output(inner_output_2.clone());
1783 let proof_2 = stark.prove(&claim_2, &aet_2).unwrap();
1784 let padded_height_2 = proof_2.padded_height().unwrap();
1785 println!("padded_height_2: {padded_height_2}");
1786
1787 let two_proofs = TwoProofs {
1788 proof1: proof_1.clone(),
1789 claim1: claim_1.clone(),
1790 proof2: proof_2.clone(),
1791 claim2: claim_2.clone(),
1792 };
1793
1794 let mut memory = HashMap::<BFieldElement, BFieldElement>::new();
1795 let outer_input = vec![];
1796 encode_to_memory(
1797 &mut memory,
1798 FIRST_NON_DETERMINISTICALLY_INITIALIZED_MEMORY_ADDRESS,
1799 &two_proofs,
1800 );
1801
1802 let mut outer_nondeterminism = NonDeterminism::new(vec![]).with_ram(memory);
1803
1804 let num_nd_digests_before = outer_nondeterminism.digests.len();
1805
1806 stark_snippet.update_nondeterminism(&mut outer_nondeterminism, &proof_1, &claim_1);
1807 stark_snippet.update_nondeterminism(&mut outer_nondeterminism, &proof_2, &claim_2);
1808
1809 let num_nd_digests_after = outer_nondeterminism.digests.len();
1810
1811 assert_eq!(
1812 num_nd_digests_after - num_nd_digests_before,
1813 stark_snippet.number_of_nondeterministic_digests_consumed(&proof_1)
1814 + stark_snippet.number_of_nondeterministic_digests_consumed(&proof_2)
1815 );
1816
1817 let program = Program::new(&verify_two_proofs_program);
1818 let vm_state = VMState::new(
1819 program.clone(),
1820 outer_input.clone().into(),
1821 outer_nondeterminism.clone(),
1822 );
1823 maybe_write_debuggable_vm_state_to_disk(&vm_state);
1824 VM::run(program, outer_input.into(), outer_nondeterminism)
1825 .expect("could not verify two STARK proofs");
1826
1827 println!(
1828 "fact({}) == {} ∧ fact({}) == {}",
1829 inner_input_1[0], inner_output_1[0], inner_input_2[0], inner_output_2[0]
1830 );
1831
1832 assert_ne!(
1833 padded_height_1, padded_height_2,
1834 "proofs do not have different padded heights"
1835 );
1836 }
1837}
1838
1839#[cfg(test)]
1840mod benches {
1841 use benches::tests::factorial_program_with_io;
1842 use benches::tests::prove_and_get_non_determinism_and_claim;
1843 use num_traits::ConstZero;
1844
1845 use super::*;
1846 use crate::generate_full_profile;
1847 use crate::linker::execute_bench;
1848 use crate::snippet_bencher::NamedBenchmarkResult;
1849 use crate::snippet_bencher::write_benchmarks;
1850 use crate::test_helpers::prepend_program_with_stack_setup;
1851 use crate::test_prelude::*;
1852 use crate::verifier::claim::shared::insert_claim_into_static_memory;
1853
1854 #[ignore = "Used for profiling the verification of a proof stored on disk."]
1855 #[macro_rules_attr::apply(test)]
1856 fn profile_from_stored_proof_output() {
1857 use std::fs::File;
1858 let stark = File::open("stark.json").expect("stark file should open read only");
1859 let stark: Stark = serde_json::from_reader(stark).unwrap();
1860 let claim_file = File::open("claim.json").expect("claim file should open read only");
1861 let claim_for_proof: Claim = serde_json::from_reader(claim_file).unwrap();
1862 let proof = File::open("proof.json").expect("proof file should open read only");
1863 let proof: Proof = serde_json::from_reader(proof).unwrap();
1864
1865 let snippet = StarkVerify {
1866 stark,
1867 memory_layout: MemoryLayout::conventional_static(),
1868 };
1869 let mut nondeterminism = NonDeterminism::new(vec![]);
1870 snippet.update_nondeterminism(&mut nondeterminism, &proof, &claim_for_proof);
1871
1872 let (claim_pointer, claim_size) =
1873 insert_claim_into_static_memory(&mut nondeterminism.ram, &claim_for_proof);
1874
1875 let default_proof_pointer = BFieldElement::ZERO;
1876
1877 let init_stack = [
1878 snippet.init_stack_for_isolated_run(),
1879 vec![claim_pointer, default_proof_pointer],
1880 ]
1881 .concat();
1882 let code = snippet.link_for_isolated_run_populated_static_memory(claim_size);
1883 let program = prepend_program_with_stack_setup(&init_stack, &Program::new(&code));
1884
1885 let name = snippet.entrypoint();
1886 let profile = generate_full_profile(
1887 &name,
1888 program,
1889 &PublicInput::new(claim_for_proof.input),
1890 &nondeterminism,
1891 );
1892 println!("{profile}");
1893 }
1894
1895 #[macro_rules_attr::apply(test)]
1896 fn benchmark_small_default_stark_static_memory() {
1897 benchmark_verifier(
1898 3,
1899 1 << 8,
1900 Stark::default(),
1901 MemoryLayout::conventional_static(),
1902 );
1903 benchmark_verifier(
1904 80,
1905 1 << 10,
1906 Stark::default(),
1907 MemoryLayout::conventional_static(),
1908 );
1909 }
1910
1911 #[macro_rules_attr::apply(test)]
1912 fn benchmark_small_default_stark_dynamic_memory() {
1913 benchmark_verifier(
1914 3,
1915 1 << 8,
1916 Stark::default(),
1917 MemoryLayout::conventional_dynamic(),
1918 );
1919 benchmark_verifier(
1920 80,
1921 1 << 10,
1922 Stark::default(),
1923 MemoryLayout::conventional_dynamic(),
1924 );
1925 }
1926
1927 #[ignore = "Takes a fairly long time. Intended to find optimal FRI expansion factor."]
1928 #[macro_rules_attr::apply(test)]
1929 fn small_benchmark_different_fri_expansion_factors() {
1930 for log2_of_fri_expansion_factor in 1..=5 {
1931 let stark = Stark::new(160, log2_of_fri_expansion_factor);
1932 benchmark_verifier(10, 1 << 8, stark, MemoryLayout::conventional_static());
1933 benchmark_verifier(40, 1 << 9, stark, MemoryLayout::conventional_static());
1934 benchmark_verifier(80, 1 << 10, stark, MemoryLayout::conventional_static());
1935 }
1936 }
1937
1938 #[ignore = "Takes a very long time. Intended to find optimal FRI expansion factor. Make sure to run
1939 with `RUSTFLAGS=\"-C opt-level=3 -C debug-assertions=no\"`"]
1940 #[macro_rules_attr::apply(test)]
1941 fn big_benchmark_different_fri_expansion_factors() {
1942 let mem_layout = MemoryLayout::conventional_static();
1943 for log2_of_fri_expansion_factor in 2..=3 {
1944 let stark = Stark::new(160, log2_of_fri_expansion_factor);
1945 benchmark_verifier(25600, 1 << 19, stark, mem_layout);
1946 benchmark_verifier(51200, 1 << 20, stark, mem_layout);
1947 benchmark_verifier(102400, 1 << 21, stark, mem_layout);
1948 }
1949 }
1950
1951 #[ignore = "Intended to generate data about verifier table heights as a function of inner padded
1952 height. Make sure to run with `RUSTFLAGS=\"-C opt-level=3 -C debug-assertions=no\"`"]
1953 #[macro_rules_attr::apply(test)]
1954 fn benchmark_verification_as_a_function_of_inner_padded_height() {
1955 for (fact_arg, expected_inner_padded_height) in [
1956 (10, 1 << 8),
1957 (40, 1 << 9),
1958 (80, 1 << 10),
1959 (100, 1 << 11),
1960 (200, 1 << 12),
1961 (400, 1 << 13),
1962 (800, 1 << 14),
1963 (1600, 1 << 15),
1964 (3200, 1 << 16),
1965 (6400, 1 << 17),
1966 (12800, 1 << 18),
1967 (25600, 1 << 19),
1968 (51200, 1 << 20),
1969 (102400, 1 << 21),
1970 ] {
1971 benchmark_verifier(
1972 fact_arg,
1973 expected_inner_padded_height,
1974 Stark::default(),
1975 MemoryLayout::conventional_static(),
1976 );
1977 }
1978 }
1979
1980 fn benchmark_verifier(
1981 factorial_argument: u32,
1982 inner_padded_height: usize,
1983 stark: Stark,
1984 mem_layout: MemoryLayout,
1985 ) {
1986 let (mut non_determinism, claim_for_proof) = prove_and_get_non_determinism_and_claim(
1987 factorial_program_with_io(),
1988 &[bfe!(factorial_argument)],
1989 NonDeterminism::default(),
1990 &stark,
1991 );
1992
1993 let claim_pointer = BFieldElement::new(1 << 30);
1994 encode_to_memory(&mut non_determinism.ram, claim_pointer, &claim_for_proof);
1995
1996 let default_proof_pointer = BFieldElement::ZERO;
1997
1998 let snippet = StarkVerify {
1999 stark,
2000 memory_layout: mem_layout,
2001 };
2002
2003 let init_stack = [
2004 snippet.init_stack_for_isolated_run(),
2005 vec![claim_pointer, default_proof_pointer],
2006 ]
2007 .concat();
2008 let code = snippet.link_for_isolated_run();
2009 let benchmark = execute_bench(&code, &init_stack, vec![], non_determinism.clone(), None);
2010 let benchmark = NamedBenchmarkResult {
2011 name: format!(
2012 "{}_inner_padded_height_{}_fri_exp_{}",
2013 snippet.entrypoint(),
2014 inner_padded_height,
2015 stark.fri_expansion_factor
2016 ),
2017 benchmark_result: benchmark,
2018 case: BenchmarkCase::CommonCase,
2019 };
2020
2021 write_benchmarks(vec![benchmark]);
2022
2023 let program = prepend_program_with_stack_setup(&init_stack, &Program::new(&code));
2024 let name = snippet.entrypoint();
2025 let profile = generate_full_profile(
2026 &name,
2027 program,
2028 &PublicInput::new(claim_for_proof.input),
2029 &non_determinism,
2030 );
2031 println!("{profile}");
2032 }
2033}