1use triton_vm::fri::AuthenticationStructure;
2use triton_vm::prelude::*;
3use triton_vm::proof_item::FriResponse;
4use triton_vm::proof_item::ProofItemVariant;
5use triton_vm::table::AuxiliaryRow;
6use triton_vm::table::MainRow;
7use triton_vm::table::QuotientSegments;
8use twenty_first::math::x_field_element::EXTENSION_DEGREE;
9use twenty_first::prelude::Polynomial;
10
11use crate::hashing::sponge_hasher::pad_and_absorb_all::PadAndAbsorbAll;
12use crate::prelude::*;
13
14const MAX_SIZE_FOR_DYNAMICALLY_SIZED_PROOF_ITEMS: u32 = 1u32 << 22;
15
16#[derive(Debug, Copy, Clone, Eq, PartialEq)]
22pub struct DequeueNextAs {
23 pub proof_item: ProofItemVariant,
24}
25
26impl DequeueNextAs {
27 pub fn new(proof_item: ProofItemVariant) -> Self {
28 Self { proof_item }
29 }
30
31 fn item_name(&self) -> String {
32 self.proof_item.to_string().to_lowercase()
33 }
34
35 fn proof_item_calculate_size(&self, library: &mut Library) -> Vec<LabelledInstruction> {
36 match self.proof_item {
37 ProofItemVariant::MasterMainTableRows => {
38 Vec::<MainRow<BFieldElement>>::compute_size_and_assert_valid_size_indicator(library)
39 }
40 ProofItemVariant::MasterAuxTableRows => {
41 Vec::<AuxiliaryRow>::compute_size_and_assert_valid_size_indicator(library)
42 }
43 ProofItemVariant::QuotientSegmentsElements => {
44 Vec::<QuotientSegments>::compute_size_and_assert_valid_size_indicator(library)
45 }
46 ProofItemVariant::FriCodeword => {
47 Vec::<XFieldElement>::compute_size_and_assert_valid_size_indicator(library)
48 }
49 ProofItemVariant::FriPolynomial => {
50 Polynomial::<XFieldElement>::compute_size_and_assert_valid_size_indicator(library)
51 }
52 ProofItemVariant::FriResponse => {
53 FriResponse::compute_size_and_assert_valid_size_indicator(library)
54 }
55 ProofItemVariant::AuthenticationStructure => {
56 AuthenticationStructure::compute_size_and_assert_valid_size_indicator(library)
59 }
60 _ => unreachable!(),
61 }
62 }
63
64 fn increment_current_item_count(&self) -> Vec<LabelledInstruction> {
71 const CURRENT_ITEM_COUNT_OFFSET: BFieldElement = BFieldElement::new(4);
72
73 triton_asm!(
74 dup 0
77 addi {CURRENT_ITEM_COUNT_OFFSET}
78 read_mem 1
81 addi 1
82 pick 1
85 addi 1
86 pick 1
87 write_mem 1
90 pop 1
91 )
93 }
94
95 fn assert_consistent_size_indicators(&self, library: &mut Library) -> Vec<LabelledInstruction> {
100 if self.proof_item.payload_static_length().is_some() {
101 return vec![];
102 }
103
104 let calculate_own_size = self.proof_item_calculate_size(library);
105
106 triton_asm! {
107 dup 0
110 addi 3
111 {&calculate_own_size}
112 hint calculated_payload_size = stack[0]
113 dup 1
116 addi 2
117 read_mem 1
118 pop 1
119 hint indicated_payload_size = stack[0]
120 dup 1
123 eq
124 assert
125 hint payload_size = stack[0]
126 addi 2
131 hint calculated_item_size = stack[0]
132 dup 1
135 read_mem 1
136 pop 1
137 eq
140 assert
141 }
143 }
144
145 fn fiat_shamir_absorb_snippet(&self, library: &mut Library) -> Vec<LabelledInstruction> {
150 if !self.proof_item.include_in_fiat_shamir_heuristic() {
151 return vec![];
152 }
153
154 let pad_and_absorb_all = library.import(Box::new(PadAndAbsorbAll));
155 triton_asm! {
156 dup 0 call {pad_and_absorb_all}
158 }
160 }
161
162 fn update_proof_item_iter_to_next_proof_item(&self) -> Vec<LabelledInstruction> {
167 triton_asm! {
168 dup 0 {&self.advance_proof_item_pointer_to_next_item()}
170 hint next_proof_item_list_element_size_pointer = stack[0]
171 swap 1 swap 2 write_mem 1 pop 1 }
176 }
177
178 fn advance_proof_item_pointer_to_next_item(&self) -> Vec<LabelledInstruction> {
183 let Some(static_length) = self.proof_item.payload_static_length() else {
184 return self.advance_proof_item_pointer_to_next_item_reading_length_from_memory();
185 };
186 Self::advance_proof_item_pointer_to_next_item_using_static_size(static_length)
187 }
188
189 fn advance_proof_item_pointer_to_next_item_using_static_size(
194 length: usize,
195 ) -> Vec<LabelledInstruction> {
196 let length_plus_bookkeeping_offset = length + 2;
197 triton_asm! {
198 push {length_plus_bookkeeping_offset}
199 hint proof_item_length_plus_bookkeeping_offset = stack[0]
200 add
201 }
202 }
203
204 fn advance_proof_item_pointer_to_next_item_reading_length_from_memory(
209 &self,
210 ) -> Vec<LabelledInstruction> {
211 triton_asm! {
212 read_mem 1 hint proof_item_length = stack[1]
214
215 push {MAX_SIZE_FOR_DYNAMICALLY_SIZED_PROOF_ITEMS}
217 dup 2
218 lt
221 assert
224 push 2 add add }
228 }
229
230 fn advance_list_element_pointer_to_proof_item_payload(&self) -> Vec<LabelledInstruction> {
235 let payload_length_indicator_size = match self.proof_item.payload_static_length() {
236 Some(_) => 0,
237 None => 1,
238 };
239 let list_item_length_indicator_size = 1;
240 let discriminant_size = 1;
241 let bookkeeping_offset =
242 payload_length_indicator_size + list_item_length_indicator_size + discriminant_size;
243
244 triton_asm! { push {bookkeeping_offset} hint bookkeeping_offset = stack[0] add }
245 }
246
247 fn verify_last_xfe_is_non_zero_if_payload_is_polynomial(&self) -> Vec<LabelledInstruction> {
257 match self.proof_item {
258 ProofItemVariant::FriPolynomial => triton_asm!(
259 dup 0
262 push 1
263 add
264 read_mem 1
265 pop 1
266 push {EXTENSION_DEGREE}
269 mul
270 push 1
271 add
272 dup 1
275 add
276 read_mem {EXTENSION_DEGREE}
279 pop 1
280 push 0
281 eq
282 swap 2
283 push 0
284 eq
285 swap 1
286 push 0
287 eq
288 add
289 add
290 push {EXTENSION_DEGREE}
291 eq
292 push 0
295 eq
296 assert
299 ),
300 _ => triton_asm!(),
301 }
302 }
303}
304
305impl BasicSnippet for DequeueNextAs {
306 fn inputs(&self) -> Vec<(DataType, String)> {
307 vec![(DataType::VoidPointer, "*proof_item_iter".to_string())]
308 }
309
310 fn outputs(&self) -> Vec<(DataType, String)> {
311 let payload_pointer_str = format!("*{}_payload", self.item_name());
312 vec![(DataType::VoidPointer, payload_pointer_str)]
313 }
314
315 fn entrypoint(&self) -> String {
316 let proof_item_name = self.item_name();
317 format!("tasmlib_verifier_vm_proof_iter_dequeue_next_as_{proof_item_name}")
318 }
319
320 fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
325 let final_hint = format!("hint {}_pointer: Pointer = stack[0]", self.item_name());
326 triton_asm! {
327 {self.entrypoint()}:
328 {&self.increment_current_item_count()}
329 read_mem 1
332 hint proof_item_list_element_size_pointer = stack[1]
333
334 addi 1
335 swap 1 addi 1
338 hint proof_item_discriminant_pointer = stack[0]
339
340 read_mem 1 hint proof_item_list_element_size_pointer = stack[0]
342 hint proof_item_discriminant = stack[1]
343
344 swap 1
345 push {self.proof_item.bfield_codec_discriminant()}
346 hint expected_proof_item_discriminant = stack[0]
347
348 eq assert {&self.assert_consistent_size_indicators(library)}
351 {&self.fiat_shamir_absorb_snippet(library)}
354 {&self.update_proof_item_iter_to_next_proof_item()}
357 {&self.advance_list_element_pointer_to_proof_item_payload()}
360 {final_hint} {&self.verify_last_xfe_is_non_zero_if_payload_is_polynomial()}
363 {final_hint} return
366 }
367 }
368}
369
370#[cfg(test)]
371mod tests {
372 use arbitrary::Arbitrary;
373 use arbitrary::Unstructured;
374 use num_traits::One;
375 use num_traits::Zero;
376 use strum::IntoEnumIterator;
377 use triton_vm::proof_item::ProofItem;
378 use triton_vm::proof_stream::ProofStream;
379 use triton_vm::table::master_table::MasterMainTable;
380 use twenty_first::prelude::*;
381
382 use super::*;
383 use crate::empty_stack;
384 use crate::execute_with_terminal_state;
385 use crate::rust_shadowing_helper_functions::dyn_malloc::dynamic_allocator;
386 use crate::structure::tasm_object::decode_from_memory_with_size;
387 use crate::test_prelude::*;
388 use crate::verifier::vm_proof_iter::shared::vm_proof_iter_struct::VmProofIter;
389
390 #[derive(Debug)]
391 struct RustShadowForDequeueNextAs<'a> {
392 dequeue_next_as: DequeueNextAs,
393
394 proof_iter_pointer: BFieldElement,
395
396 stack: &'a mut Vec<BFieldElement>,
397 memory: &'a mut HashMap<BFieldElement, BFieldElement>,
398 sponge: &'a mut Tip5,
399 }
400
401 impl RustShadowForDequeueNextAs<'_> {
402 fn execute(&mut self) -> Vec<BFieldElement> {
403 self.assert_correct_discriminant();
404 self.maybe_alter_fiat_shamir_heuristic_with_proof_item();
405 self.update_stack_using_current_proof_item();
406 self.update_proof_item_iter();
407 self.update_current_item_count();
408 self.public_output()
409 }
410
411 fn current_proof_item_list_element_size_pointer(&self) -> BFieldElement {
412 let &maybe_pointer = self.memory.get(&self.proof_iter_pointer).unwrap();
413 maybe_pointer
414 }
415
416 fn current_proof_item_list_element_size(&self) -> BFieldElement {
417 let Some(item_length) = self.dequeue_next_as.proof_item.payload_static_length() else {
418 return self.fetch_current_proof_item_list_element_size_from_memory();
419 };
420 let discriminant_length = 1;
421 BFieldElement::new((item_length + discriminant_length) as u64)
422 }
423
424 fn fetch_current_proof_item_list_element_size_from_memory(&self) -> BFieldElement {
426 let size_pointer = self.current_proof_item_list_element_size_pointer();
427 let &element_size = self.memory.get(&size_pointer).unwrap();
428 assert!(
429 element_size.value() < MAX_SIZE_FOR_DYNAMICALLY_SIZED_PROOF_ITEMS as u64,
430 "proof item size may not exceed max allowed size"
431 );
432 element_size
433 }
434
435 fn next_proof_item_list_element_size_pointer(&self) -> BFieldElement {
436 self.current_proof_item_list_element_size_pointer()
437 + self.current_proof_item_list_element_size()
438 + BFieldElement::one()
439 }
440
441 fn discriminant_pointer(&self) -> BFieldElement {
442 self.current_proof_item_list_element_size_pointer() + BFieldElement::one()
443 }
444
445 fn discriminant(&self) -> BFieldElement {
446 let &discriminant = self.memory.get(&self.discriminant_pointer()).unwrap();
447 discriminant
448 }
449
450 fn proof_item_payload_pointer(&self) -> BFieldElement {
451 self.discriminant_pointer()
452 + BFieldElement::one()
453 + self.proof_item_payload_size_indicator_length()
454 }
455
456 fn proof_item_payload_size_indicator_length(&self) -> BFieldElement {
457 match self.dequeue_next_as.proof_item.payload_static_length() {
458 Some(_) => BFieldElement::zero(),
459 None => BFieldElement::one(),
460 }
461 }
462
463 fn assert_correct_discriminant(&self) {
464 let expected_discriminant = self.dequeue_next_as.proof_item.bfield_codec_discriminant();
465 assert_eq!(expected_discriminant, self.discriminant().value() as usize);
466 }
467
468 fn proof_item(&self) -> ProofItem {
469 let maybe_item = decode_from_memory_with_size::<ProofItem>(
470 self.memory,
471 self.discriminant_pointer(),
472 self.current_proof_item_list_element_size().value() as usize,
473 );
474 *maybe_item.unwrap()
475 }
476
477 fn maybe_alter_fiat_shamir_heuristic_with_proof_item(&mut self) {
478 let proof_item = self.proof_item();
479 if proof_item.include_in_fiat_shamir_heuristic() {
480 Tip5::pad_and_absorb_all(self.sponge, &proof_item.encode());
481 }
482 }
483
484 fn update_stack_using_current_proof_item(&mut self) {
485 self.stack.push(self.proof_item_payload_pointer());
486 }
487
488 fn update_proof_item_iter(&mut self) {
489 let next_item_pointer = self.next_proof_item_list_element_size_pointer();
490 self.memory
491 .insert(self.proof_iter_pointer, next_item_pointer);
492 }
493
494 fn update_current_item_count(&mut self) {
495 let count = self
496 .memory
497 .get_mut(&(self.proof_iter_pointer + bfe!(4)))
498 .unwrap();
499 count.increment();
500 }
501
502 fn public_output(&self) -> Vec<BFieldElement> {
503 vec![]
504 }
505 }
506
507 impl Procedure for DequeueNextAs {
508 fn rust_shadow(
509 &self,
510 stack: &mut Vec<BFieldElement>,
511 memory: &mut HashMap<BFieldElement, BFieldElement>,
512 _: &NonDeterminism,
513 _: &[BFieldElement],
514 sponge: &mut Option<Tip5>,
515 ) -> Vec<BFieldElement> {
516 RustShadowForDequeueNextAs {
517 dequeue_next_as: *self,
518 proof_iter_pointer: stack.pop().unwrap(),
519 stack,
520 memory,
521 sponge: sponge.as_mut().unwrap(),
522 }
523 .execute()
524 }
525
526 fn pseudorandom_initial_state(
527 &self,
528 seed: [u8; 32],
529 _: Option<BenchmarkCase>,
530 ) -> ProcedureInitialState {
531 let mut rng = StdRng::from_seed(seed);
532 let mut proof_stream = ProofStream::new();
533 proof_stream.enqueue(Self::pseudorandom_proof_item(self.proof_item, rng.random()));
534
535 let other_item_type = ProofItemVariant::iter().choose(&mut rng).unwrap();
536 proof_stream.enqueue(Self::pseudorandom_proof_item(other_item_type, rng.random()));
537
538 self.initial_state_from_proof_and_address(proof_stream.into(), rng.random())
539 }
540 }
541
542 impl DequeueNextAs {
543 fn initial_state_from_proof_and_address(
544 &self,
545 proof: Proof,
546 proof_address: BFieldElement,
547 ) -> ProcedureInitialState {
548 let mut ram = HashMap::new();
549 encode_to_memory(&mut ram, proof_address, &proof);
550
551 let proof_iter_address = dynamic_allocator(&mut ram);
552 let vm_proof_iter_init_state = VmProofIter::new(proof_address, &proof);
553 encode_to_memory(&mut ram, proof_iter_address, &vm_proof_iter_init_state);
554
555 ProcedureInitialState {
556 stack: [self.init_stack_for_isolated_run(), vec![proof_iter_address]].concat(),
557 nondeterminism: NonDeterminism::default().with_ram(ram),
558 public_input: vec![],
559 sponge: Some(Tip5::init()),
560 }
561 }
562
563 fn pseudorandom_new(seed: [u8; 32]) -> Self {
564 let mut unstructured = Unstructured::new(&seed);
565 let proof_item: ProofItem = Arbitrary::arbitrary(&mut unstructured).unwrap();
566 let proof_item = proof_item.into();
567 Self { proof_item }
568 }
569
570 pub(crate) fn pseudorandom_proof_stream(
571 proof_items_variants: Vec<ProofItemVariant>,
572 seed: [u8; 32],
573 ) -> ProofStream {
574 let mut rng = StdRng::from_seed(seed);
575
576 let mut proof_stream = ProofStream::new();
577 for &proof_item in &proof_items_variants {
578 let item = DequeueNextAs::pseudorandom_proof_item(proof_item, rng.random());
579 proof_stream.enqueue(item);
580 }
581
582 proof_stream
583 }
584
585 fn pseudorandom_proof_item(
586 proof_item_variant: ProofItemVariant,
587 seed: [u8; 32],
588 ) -> ProofItem {
589 let mut rng = StdRng::from_seed(seed);
590 let proof_stream_seed: [u8; 10000] = rng.random();
591 let mut unstructured = Unstructured::new(&proof_stream_seed);
592
593 use ProofItemVariant::*;
594 match proof_item_variant {
595 MerkleRoot => {
596 ProofItem::MerkleRoot(Arbitrary::arbitrary(&mut unstructured).unwrap())
597 }
598 OutOfDomainMainRow => {
599 ProofItem::OutOfDomainMainRow(Arbitrary::arbitrary(&mut unstructured).unwrap())
600 }
601 OutOfDomainAuxRow => {
602 ProofItem::OutOfDomainAuxRow(Arbitrary::arbitrary(&mut unstructured).unwrap())
603 }
604 OutOfDomainQuotientSegments => ProofItem::OutOfDomainQuotientSegments(
605 Arbitrary::arbitrary(&mut unstructured).unwrap(),
606 ),
607 AuthenticationStructure => ProofItem::AuthenticationStructure(
608 Arbitrary::arbitrary(&mut unstructured).unwrap(),
609 ),
610 MasterMainTableRows => {
611 ProofItem::MasterMainTableRows(Arbitrary::arbitrary(&mut unstructured).unwrap())
612 }
613 MasterAuxTableRows => {
614 ProofItem::MasterAuxTableRows(Arbitrary::arbitrary(&mut unstructured).unwrap())
615 }
616 Log2PaddedHeight => {
617 ProofItem::Log2PaddedHeight(Arbitrary::arbitrary(&mut unstructured).unwrap())
618 }
619 QuotientSegmentsElements => ProofItem::QuotientSegmentsElements(
620 Arbitrary::arbitrary(&mut unstructured).unwrap(),
621 ),
622 FriCodeword => {
623 ProofItem::FriCodeword(Arbitrary::arbitrary(&mut unstructured).unwrap())
624 }
625 FriPolynomial => {
626 ProofItem::FriPolynomial(Arbitrary::arbitrary(&mut unstructured).unwrap())
627 }
628 FriResponse => {
629 let fri_response = triton_vm::proof_item::FriResponse {
631 auth_structure: vec![],
632 revealed_leaves: Arbitrary::arbitrary(&mut unstructured).unwrap(),
633 };
634 ProofItem::FriResponse(fri_response)
635 }
636 }
637 }
638
639 fn test_rust_equivalence(self, initial_state: ProcedureInitialState) {
640 test_rust_equivalence_given_complete_state(
641 &ShadowedProcedure::new(self),
642 &initial_state.stack,
643 &initial_state.public_input,
644 &initial_state.nondeterminism,
645 &initial_state.sponge,
646 None,
647 );
648 }
649 }
650
651 #[test]
652 fn disallow_too_big_dynamically_sized_proof_item() {
653 let dequeue_next_as = DequeueNextAs::new(ProofItemVariant::MasterMainTableRows);
654 let initial_state = initial_state_with_too_big_master_table_rows();
655 let tvm_result = execute_with_terminal_state(
656 Program::new(&dequeue_next_as.link_for_isolated_run()),
657 &[],
658 &initial_state.stack,
659 &initial_state.nondeterminism,
660 None,
661 );
662
663 let rust_result = std::panic::catch_unwind(|| {
664 let mut stack = initial_state.stack.clone();
665 let mut memory = initial_state.nondeterminism.ram.clone();
666 dequeue_next_as.rust_shadow(
667 &mut stack,
668 &mut memory,
669 &NonDeterminism::default(),
670 &[],
671 &mut None,
672 );
673 });
674
675 assert!(
676 rust_result.is_err() && tvm_result.is_err(),
677 "Test case: Too big dynamically-sized proof item must fail on both platforms."
678 );
679 let err = tvm_result.unwrap_err();
680 assert!(matches!(err, InstructionError::AssertionFailed(_)));
681 }
682
683 fn initial_state_with_too_big_master_table_rows() -> ProcedureInitialState {
684 let dummy_master_table_rows = vec![[bfe!(101); MasterMainTable::NUM_COLUMNS]; 15000];
685 let proof_item = ProofItem::MasterMainTableRows(dummy_master_table_rows);
686 let mut proof_stream = ProofStream::new();
687 proof_stream.enqueue(proof_item);
688 let proof_ptr = BFieldElement::zero();
689 DequeueNextAs::new(ProofItemVariant::MasterMainTableRows)
690 .initial_state_from_proof_and_address(proof_stream.into(), proof_ptr)
691 }
692
693 #[test]
694 fn disallow_trailing_zeros_in_xfe_poly_encoding() {
695 let dequeue_next_as = DequeueNextAs::new(ProofItemVariant::FriPolynomial);
696 let initial_state = initial_state_with_trailing_zeros_in_xfe_poly_encoding();
697 let code = dequeue_next_as.link_for_isolated_run();
698 let tvm_result = execute_with_terminal_state(
699 Program::new(&code),
700 &[],
701 &initial_state.stack,
702 &initial_state.nondeterminism,
703 None,
704 );
705
706 let rust_result = std::panic::catch_unwind(|| {
707 let mut stack = initial_state.stack.clone();
708 let mut memory = initial_state.nondeterminism.ram.clone();
709 dequeue_next_as.rust_shadow(
710 &mut stack,
711 &mut memory,
712 &NonDeterminism::default(),
713 &[],
714 &mut None,
715 );
716 });
717
718 assert!(
719 rust_result.is_err() && tvm_result.is_err(),
720 "Test case: Too big dynamically-sized proof item must fail on both platforms."
721 );
722 let err = tvm_result.unwrap_err();
723 assert!(matches!(err, InstructionError::AssertionFailed(_)));
724 }
725
726 fn initial_state_with_trailing_zeros_in_xfe_poly_encoding() -> ProcedureInitialState {
727 let mut encoded_fri_poly_proof_item =
728 ProofItem::FriPolynomial(Polynomial::new(vec![xfe!(1), xfe!(2), xfe!(3), xfe!(100)]))
729 .encode();
730
731 let encoding_length = encoded_fri_poly_proof_item.len();
733 for i in 0..EXTENSION_DEGREE {
734 encoded_fri_poly_proof_item[encoding_length - 1 - i] = bfe!(0);
735 }
736
737 let proof_start_address = BFieldElement::zero();
738
739 let mut ram = HashMap::new();
740
741 let mut address = proof_start_address;
742
743 ram.insert(address, bfe!(encoding_length as u64 + 2));
745 address.increment();
746
747 ram.insert(address, bfe!(1));
749 address.increment();
750
751 ram.insert(address, bfe!(encoding_length as u64));
753 address.increment();
754
755 for word in encoded_fri_poly_proof_item {
757 ram.insert(address, word);
758 address.increment();
759 }
760
761 let address_of_first_element = proof_start_address + BFieldElement::new(2);
763 let proof_iter_address = proof_start_address - BFieldElement::one();
764 ram.insert(proof_iter_address, address_of_first_element);
765 ProcedureInitialState {
766 stack: [empty_stack(), vec![proof_iter_address]].concat(),
767 nondeterminism: NonDeterminism::default().with_ram(ram),
768 public_input: vec![],
769 sponge: Some(Tip5::init()),
770 }
771 }
772
773 fn dequeueing_is_equivalent_in_rust_and_tasm_prop(proof_item_variant: ProofItemVariant) {
774 let mut proof_stream = ProofStream::new();
775 let proof_item = DequeueNextAs::pseudorandom_proof_item(proof_item_variant, rand::random());
776 proof_stream.enqueue(proof_item);
777
778 let dequeue_next_as = DequeueNextAs::new(proof_item_variant);
779 let proof_ptr = BFieldElement::new(14);
780 let init_state =
781 dequeue_next_as.initial_state_from_proof_and_address(proof_stream.into(), proof_ptr);
782 dequeue_next_as.test_rust_equivalence(init_state);
783 }
784
785 #[test]
786 fn dequeueing_all_proof_items_individually_is_equivalent_in_rust_and_tasm() {
787 for variant in ProofItemVariant::iter() {
788 dequeueing_is_equivalent_in_rust_and_tasm_prop(variant);
789 }
790 }
791
792 #[proptest]
793 fn dequeuing_as_suggested_element_is_equivalent_in_rust_and_tasm(seed: [u8; 32]) {
794 let dequeue_next_as = DequeueNextAs::pseudorandom_new(seed);
795 let initial_state = dequeue_next_as.pseudorandom_initial_state(seed, None);
796 dequeue_next_as.test_rust_equivalence(initial_state);
797 }
798
799 #[derive(Debug, Default, Clone, Eq, PartialEq)]
801 struct TestHelperDequeueMultipleAs {
802 proof_items: Vec<ProofItemVariant>,
803 }
804
805 impl BasicSnippet for TestHelperDequeueMultipleAs {
806 fn inputs(&self) -> Vec<(DataType, String)> {
807 vec![(DataType::VoidPointer, "*proof_item_iter".to_string())]
808 }
809
810 fn outputs(&self) -> Vec<(DataType, String)> {
811 vec![(DataType::VoidPointer, "*proof_item_iter".to_string())]
812 }
813
814 fn entrypoint(&self) -> String {
815 "test_helper_dequeue_multiple_as".to_string()
816 }
817
818 fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
819 let dequeue_next_as_entrypoints = self
820 .proof_items
821 .iter()
822 .map(|&item| library.import(Box::new(DequeueNextAs::new(item))))
823 .collect::<Vec<_>>();
824
825 let code_body = dequeue_next_as_entrypoints
826 .into_iter()
827 .flat_map(|snippet| triton_asm!(dup 0 call {snippet} pop 1))
828 .collect_vec();
829
830 triton_asm!({self.entrypoint()}: {&code_body} return)
831 }
832 }
833
834 impl Procedure for TestHelperDequeueMultipleAs {
835 fn rust_shadow(
836 &self,
837 stack: &mut Vec<BFieldElement>,
838 memory: &mut HashMap<BFieldElement, BFieldElement>,
839 non_determinism: &NonDeterminism,
840 std_in: &[BFieldElement],
841 sponge: &mut Option<Tip5>,
842 ) -> Vec<BFieldElement> {
843 let &proof_iter_pointer = stack.last().unwrap();
844 for &proof_item in &self.proof_items {
845 let dequeue_next_as = DequeueNextAs { proof_item };
846 stack.push(proof_iter_pointer);
847 dequeue_next_as.rust_shadow(stack, memory, non_determinism, std_in, sponge);
848 stack.pop().unwrap();
849 }
850 vec![]
851 }
852
853 fn pseudorandom_initial_state(
854 &self,
855 seed: [u8; 32],
856 _: Option<BenchmarkCase>,
857 ) -> ProcedureInitialState {
858 let mut rng = StdRng::from_seed(seed);
859 let proof_stream =
860 DequeueNextAs::pseudorandom_proof_stream(self.proof_items.clone(), rng.random());
861
862 let dummy_snippet_for_state_init = DequeueNextAs::new(ProofItemVariant::MerkleRoot);
866 dummy_snippet_for_state_init
867 .initial_state_from_proof_and_address(proof_stream.into(), rng.random())
868 }
869 }
870
871 impl TestHelperDequeueMultipleAs {
872 fn test_rust_equivalence(self, initial_state: ProcedureInitialState) {
873 test_rust_equivalence_given_complete_state(
874 &ShadowedProcedure::new(self),
875 &initial_state.stack,
876 &initial_state.public_input,
877 &initial_state.nondeterminism,
878 &initial_state.sponge,
879 None,
880 );
881 }
882 }
883
884 #[test]
885 fn dequeue_two() {
886 let proof_items = vec![ProofItemVariant::MerkleRoot, ProofItemVariant::MerkleRoot];
887 let dequeue_multiple = TestHelperDequeueMultipleAs { proof_items };
888 let initial_state = dequeue_multiple.pseudorandom_initial_state([0; 32], None);
889 dequeue_multiple.test_rust_equivalence(initial_state);
890 }
891
892 #[proptest]
893 fn dequeue_multiple(#[strategy(arb())] proof_items: Vec<ProofItem>, seed: [u8; 32]) {
894 let proof_items = proof_items.into_iter().map(|i| i.into()).collect_vec();
895 let dequeue_multiple = TestHelperDequeueMultipleAs { proof_items };
896 let initial_state = dequeue_multiple.pseudorandom_initial_state(seed, None);
897 dequeue_multiple.test_rust_equivalence(initial_state);
898 }
899}