1use std::collections::HashMap;
2use std::collections::VecDeque;
3use std::fmt::Display;
4use std::fmt::Formatter;
5use std::fmt::Result as FmtResult;
6use std::ops::Deref;
7use std::ops::Range;
8
9use air::table::hash::PermutationTrace;
10use air::table::processor::NUM_HELPER_VARIABLE_REGISTERS;
11use air::table_column::MasterMainColumn;
12use air::table_column::ProcessorMainColumn;
13use arbitrary::Arbitrary;
14use isa::error::AssertionError;
15use isa::error::InstructionError;
16use isa::error::OpStackError;
17use isa::instruction::Instruction;
18use isa::op_stack::NumberOfWords;
19use isa::op_stack::OpStack;
20use isa::op_stack::OpStackElement;
21use isa::op_stack::UnderflowIO;
22use isa::program::Program;
23use itertools::Itertools;
24use ndarray::Array1;
25use num_traits::ConstOne;
26use num_traits::ConstZero;
27use num_traits::Zero;
28use serde::Deserialize;
29use serde::Serialize;
30use strum::EnumCount;
31use twenty_first::math::x_field_element::EXTENSION_DEGREE;
32use twenty_first::prelude::*;
33use twenty_first::util_types::sponge;
34
35use crate::aet::AlgebraicExecutionTrace;
36use crate::error::VMError;
37use crate::execution_trace_profiler::ExecutionTraceProfile;
38use crate::execution_trace_profiler::ExecutionTraceProfiler;
39use crate::profiler::profiler;
40use crate::table::op_stack::OpStackTableEntry;
41use crate::table::ram::RamTableCall;
42use crate::table::u32::U32TableEntry;
43
44type VMResult<T> = Result<T, VMError>;
45type InstructionResult<T> = Result<T, InstructionError>;
46
47#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize, Arbitrary)]
48pub struct VM;
49
50#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Arbitrary)]
51pub struct VMState {
52 pub program: Program,
55
56 pub public_input: VecDeque<BFieldElement>,
59
60 pub public_output: Vec<BFieldElement>,
63
64 pub secret_individual_tokens: VecDeque<BFieldElement>,
67
68 pub secret_digests: VecDeque<Digest>,
70
71 pub ram: HashMap<BFieldElement, BFieldElement>,
74
75 ram_calls: Vec<RamTableCall>,
76
77 pub op_stack: OpStack,
79
80 pub jump_stack: Vec<(BFieldElement, BFieldElement)>,
82
83 pub cycle_count: u32,
85
86 pub instruction_pointer: usize,
88
89 pub sponge: Option<Tip5>,
102
103 pub halting: bool,
105}
106
107#[derive(Debug, Clone, Eq, PartialEq)]
110pub enum CoProcessorCall {
111 SpongeStateReset,
112
113 Tip5Trace(Instruction, Box<PermutationTrace>),
119
120 U32(U32TableEntry),
121
122 OpStack(OpStackTableEntry),
123
124 Ram(RamTableCall),
125}
126
127impl VM {
128 pub fn run(
138 program: Program,
139 public_input: PublicInput,
140 non_determinism: NonDeterminism,
141 ) -> VMResult<Vec<BFieldElement>> {
142 let mut state = VMState::new(program, public_input, non_determinism);
143 if let Err(err) = state.run() {
144 return Err(VMError::new(err, state));
145 }
146 Ok(state.public_output)
147 }
148
149 pub fn trace_execution(
161 program: Program,
162 public_input: PublicInput,
163 non_determinism: NonDeterminism,
164 ) -> VMResult<(AlgebraicExecutionTrace, Vec<BFieldElement>)> {
165 profiler!(start "trace execution" ("gen"));
166 let state = VMState::new(program, public_input, non_determinism);
167 let (aet, terminal_state) = Self::trace_execution_of_state(state)?;
168 profiler!(stop "trace execution");
169 Ok((aet, terminal_state.public_output))
170 }
171
172 pub fn trace_execution_of_state(
179 mut state: VMState,
180 ) -> VMResult<(AlgebraicExecutionTrace, VMState)> {
181 let mut aet = AlgebraicExecutionTrace::new(state.program.clone());
182
183 while !state.halting {
184 if let Err(err) = aet.record_state(&state) {
185 return Err(VMError::new(err, state));
186 };
187 let co_processor_calls = match state.step() {
188 Ok(calls) => calls,
189 Err(err) => return Err(VMError::new(err, state)),
190 };
191 for call in co_processor_calls {
192 aet.record_co_processor_call(call);
193 }
194 }
195
196 Ok((aet, state))
197 }
198
199 pub fn profile(
210 program: Program,
211 public_input: PublicInput,
212 non_determinism: NonDeterminism,
213 ) -> VMResult<(Vec<BFieldElement>, ExecutionTraceProfile)> {
214 let mut profiler = ExecutionTraceProfiler::new();
215 let mut state = VMState::new(program.clone(), public_input, non_determinism);
216 let mut previous_jump_stack_len = state.jump_stack.len();
217 let mut aet = AlgebraicExecutionTrace::new(state.program.clone());
218 while !state.halting {
219 if let Err(err) = aet.record_state(&state) {
220 return Err(VMError::new(err, state));
221 };
222 if let Ok(Instruction::Call(address)) = state.current_instruction() {
223 let label = program.label_for_address(address.value());
224 profiler.enter_span(label, &aet);
225 }
226
227 let co_processor_calls = match state.step() {
228 Ok(calls) => calls,
229 Err(err) => return Err(VMError::new(err, state)),
230 };
231 for call in co_processor_calls {
232 aet.record_co_processor_call(call);
233 }
234
235 if state.jump_stack.len() < previous_jump_stack_len {
236 profiler.exit_span(&aet);
237 }
238 previous_jump_stack_len = state.jump_stack.len();
239 }
240
241 Ok((state.public_output, profiler.finish(&aet)))
242 }
243}
244
245impl VMState {
246 pub fn new(
248 program: Program,
249 public_input: PublicInput,
250 non_determinism: NonDeterminism,
251 ) -> Self {
252 let program_digest = program.hash();
253
254 Self {
255 program,
256 public_input: public_input.individual_tokens.into(),
257 public_output: vec![],
258 secret_individual_tokens: non_determinism.individual_tokens.into(),
259 secret_digests: non_determinism.digests.into(),
260 ram: non_determinism.ram,
261 ram_calls: vec![],
262 op_stack: OpStack::new(program_digest),
263 jump_stack: vec![],
264 cycle_count: 0,
265 instruction_pointer: 0,
266 sponge: None,
267 halting: false,
268 }
269 }
270
271 pub fn derive_helper_variables(&self) -> [BFieldElement; NUM_HELPER_VARIABLE_REGISTERS] {
272 let mut hvs = bfe_array![0; NUM_HELPER_VARIABLE_REGISTERS];
273 let Ok(current_instruction) = self.current_instruction() else {
274 return hvs;
275 };
276
277 let decompose_arg = |a: u64| bfe_array![a % 2, (a >> 1) % 2, (a >> 2) % 2, (a >> 3) % 2];
278 let ram_read = |address| self.ram.get(&address).copied().unwrap_or_else(|| bfe!(0));
279
280 match current_instruction {
281 Instruction::Pop(_)
282 | Instruction::Divine(_)
283 | Instruction::Pick(_)
284 | Instruction::Place(_)
285 | Instruction::Dup(_)
286 | Instruction::Swap(_)
287 | Instruction::ReadMem(_)
288 | Instruction::WriteMem(_)
289 | Instruction::ReadIo(_)
290 | Instruction::WriteIo(_) => {
291 let arg = current_instruction.arg().unwrap().value();
292 hvs[..4].copy_from_slice(&decompose_arg(arg));
293 }
294 Instruction::Skiz => {
295 let st0 = self.op_stack[0];
296 hvs[0] = st0.inverse_or_zero();
297 let next_opcode = self.next_instruction_or_argument().value();
298 let decomposition = Self::decompose_opcode_for_instruction_skiz(next_opcode);
299 hvs[1..6].copy_from_slice(&decomposition);
300 }
301 Instruction::RecurseOrReturn => {
302 hvs[0] = (self.op_stack[6] - self.op_stack[5]).inverse_or_zero()
303 }
304 Instruction::SpongeAbsorbMem => {
305 hvs[0] = ram_read(self.op_stack[0] + bfe!(4));
306 hvs[1] = ram_read(self.op_stack[0] + bfe!(5));
307 hvs[2] = ram_read(self.op_stack[0] + bfe!(6));
308 hvs[3] = ram_read(self.op_stack[0] + bfe!(7));
309 hvs[4] = ram_read(self.op_stack[0] + bfe!(8));
310 hvs[5] = ram_read(self.op_stack[0] + bfe!(9));
311 }
312 Instruction::MerkleStep => {
313 let divined_digest = self.secret_digests.front().copied().unwrap_or_default();
314 let node_index = self.op_stack[5].value();
315 hvs[..5].copy_from_slice(&divined_digest.values());
316 hvs[5] = bfe!(node_index % 2);
317 }
318 Instruction::MerkleStepMem => {
319 let node_index = self.op_stack[5].value();
320 let ram_pointer = self.op_stack[7];
321 hvs[0] = ram_read(ram_pointer);
322 hvs[1] = ram_read(ram_pointer + bfe!(1));
323 hvs[2] = ram_read(ram_pointer + bfe!(2));
324 hvs[3] = ram_read(ram_pointer + bfe!(3));
325 hvs[4] = ram_read(ram_pointer + bfe!(4));
326 hvs[5] = bfe!(node_index % 2);
327 }
328 Instruction::Split => {
329 let top_of_stack = self.op_stack[0].value();
330 let lo = bfe!(top_of_stack & 0xffff_ffff);
331 let hi = bfe!(top_of_stack >> 32);
332 if !lo.is_zero() {
333 let max_val_of_hi = bfe!(2_u64.pow(32) - 1);
334 hvs[0] = (hi - max_val_of_hi).inverse_or_zero();
335 }
336 }
337 Instruction::Eq => hvs[0] = (self.op_stack[1] - self.op_stack[0]).inverse_or_zero(),
338 Instruction::XxDotStep => {
339 hvs[0] = ram_read(self.op_stack[0]);
340 hvs[1] = ram_read(self.op_stack[0] + bfe!(1));
341 hvs[2] = ram_read(self.op_stack[0] + bfe!(2));
342 hvs[3] = ram_read(self.op_stack[1]);
343 hvs[4] = ram_read(self.op_stack[1] + bfe!(1));
344 hvs[5] = ram_read(self.op_stack[1] + bfe!(2));
345 }
346 Instruction::XbDotStep => {
347 hvs[0] = ram_read(self.op_stack[0]);
348 hvs[1] = ram_read(self.op_stack[1]);
349 hvs[2] = ram_read(self.op_stack[1] + bfe!(1));
350 hvs[3] = ram_read(self.op_stack[1] + bfe!(2));
351 }
352 _ => (),
353 }
354
355 hvs
356 }
357
358 fn decompose_opcode_for_instruction_skiz(opcode: u64) -> [BFieldElement; 5] {
359 bfe_array![
360 opcode % 2,
361 (opcode >> 1) % 4,
362 (opcode >> 3) % 4,
363 (opcode >> 5) % 4,
364 opcode >> 7,
365 ]
366 }
367
368 pub fn step(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
370 if self.halting {
371 return Err(InstructionError::MachineHalted);
372 }
373
374 let current_instruction = self.current_instruction()?;
375 let op_stack_delta = current_instruction.op_stack_size_influence();
376 if self.op_stack.would_be_too_shallow(op_stack_delta) {
377 return Err(InstructionError::OpStackError(OpStackError::TooShallow));
378 }
379
380 self.start_recording_op_stack_calls();
381 let mut co_processor_calls = match current_instruction {
382 Instruction::Pop(n) => self.pop(n)?,
383 Instruction::Push(field_element) => self.push(field_element),
384 Instruction::Divine(n) => self.divine(n)?,
385 Instruction::Pick(stack_element) => self.pick(stack_element),
386 Instruction::Place(stack_element) => self.place(stack_element)?,
387 Instruction::Dup(stack_element) => self.dup(stack_element),
388 Instruction::Swap(stack_element) => self.swap(stack_element),
389 Instruction::Halt => self.halt(),
390 Instruction::Nop => self.nop(),
391 Instruction::Skiz => self.skiz()?,
392 Instruction::Call(address) => self.call(address),
393 Instruction::Return => self.return_from_call()?,
394 Instruction::Recurse => self.recurse()?,
395 Instruction::RecurseOrReturn => self.recurse_or_return()?,
396 Instruction::Assert => self.assert()?,
397 Instruction::ReadMem(n) => self.read_mem(n)?,
398 Instruction::WriteMem(n) => self.write_mem(n)?,
399 Instruction::Hash => self.hash()?,
400 Instruction::SpongeInit => self.sponge_init(),
401 Instruction::SpongeAbsorb => self.sponge_absorb()?,
402 Instruction::SpongeAbsorbMem => self.sponge_absorb_mem()?,
403 Instruction::SpongeSqueeze => self.sponge_squeeze()?,
404 Instruction::AssertVector => self.assert_vector()?,
405 Instruction::Add => self.add()?,
406 Instruction::AddI(field_element) => self.addi(field_element),
407 Instruction::Mul => self.mul()?,
408 Instruction::Invert => self.invert()?,
409 Instruction::Eq => self.eq()?,
410 Instruction::Split => self.split()?,
411 Instruction::Lt => self.lt()?,
412 Instruction::And => self.and()?,
413 Instruction::Xor => self.xor()?,
414 Instruction::Log2Floor => self.log_2_floor()?,
415 Instruction::Pow => self.pow()?,
416 Instruction::DivMod => self.div_mod()?,
417 Instruction::PopCount => self.pop_count()?,
418 Instruction::XxAdd => self.xx_add()?,
419 Instruction::XxMul => self.xx_mul()?,
420 Instruction::XInvert => self.x_invert()?,
421 Instruction::XbMul => self.xb_mul()?,
422 Instruction::WriteIo(n) => self.write_io(n)?,
423 Instruction::ReadIo(n) => self.read_io(n)?,
424 Instruction::MerkleStep => self.merkle_step_non_determinism()?,
425 Instruction::MerkleStepMem => self.merkle_step_mem()?,
426 Instruction::XxDotStep => self.xx_dot_step()?,
427 Instruction::XbDotStep => self.xb_dot_step()?,
428 };
429 let op_stack_calls = self.stop_recording_op_stack_calls();
430 co_processor_calls.extend(op_stack_calls);
431
432 self.cycle_count += 1;
433
434 Ok(co_processor_calls)
435 }
436
437 fn start_recording_op_stack_calls(&mut self) {
438 self.op_stack.start_recording_underflow_io_sequence();
439 }
440
441 fn stop_recording_op_stack_calls(&mut self) -> Vec<CoProcessorCall> {
442 let sequence = self.op_stack.stop_recording_underflow_io_sequence();
443 self.underflow_io_sequence_to_co_processor_calls(sequence)
444 }
445
446 fn underflow_io_sequence_to_co_processor_calls(
447 &self,
448 underflow_io_sequence: Vec<UnderflowIO>,
449 ) -> Vec<CoProcessorCall> {
450 let op_stack_table_entries = OpStackTableEntry::from_underflow_io_sequence(
451 self.cycle_count,
452 self.op_stack.pointer(),
453 underflow_io_sequence,
454 );
455 op_stack_table_entries
456 .into_iter()
457 .map(CoProcessorCall::OpStack)
458 .collect()
459 }
460
461 fn start_recording_ram_calls(&mut self) {
462 self.ram_calls.clear();
463 }
464
465 fn stop_recording_ram_calls(&mut self) -> Vec<CoProcessorCall> {
466 self.ram_calls.drain(..).map(CoProcessorCall::Ram).collect()
467 }
468
469 fn pop(&mut self, n: NumberOfWords) -> InstructionResult<Vec<CoProcessorCall>> {
470 for _ in 0..n.num_words() {
471 self.op_stack.pop()?;
472 }
473
474 self.instruction_pointer += 2;
475 Ok(vec![])
476 }
477
478 fn push(&mut self, element: BFieldElement) -> Vec<CoProcessorCall> {
479 self.op_stack.push(element);
480
481 self.instruction_pointer += 2;
482 vec![]
483 }
484
485 fn divine(&mut self, n: NumberOfWords) -> InstructionResult<Vec<CoProcessorCall>> {
486 let input_len = self.secret_individual_tokens.len();
487 if input_len < n.num_words() {
488 return Err(InstructionError::EmptySecretInput(input_len));
489 }
490 for _ in 0..n.num_words() {
491 let element = self.secret_individual_tokens.pop_front().unwrap();
492 self.op_stack.push(element);
493 }
494
495 self.instruction_pointer += 2;
496 Ok(vec![])
497 }
498
499 fn pick(&mut self, stack_register: OpStackElement) -> Vec<CoProcessorCall> {
500 let element = self.op_stack.remove(stack_register);
501 self.op_stack.push(element);
502
503 self.instruction_pointer += 2;
504 vec![]
505 }
506
507 fn place(&mut self, stack_register: OpStackElement) -> InstructionResult<Vec<CoProcessorCall>> {
508 let element = self.op_stack.pop()?;
509 self.op_stack.insert(stack_register, element);
510
511 self.instruction_pointer += 2;
512 Ok(vec![])
513 }
514
515 fn dup(&mut self, stack_register: OpStackElement) -> Vec<CoProcessorCall> {
516 let element = self.op_stack[stack_register];
517 self.op_stack.push(element);
518
519 self.instruction_pointer += 2;
520 vec![]
521 }
522
523 fn swap(&mut self, st: OpStackElement) -> Vec<CoProcessorCall> {
524 (self.op_stack[0], self.op_stack[st]) = (self.op_stack[st], self.op_stack[0]);
525 self.instruction_pointer += 2;
526 vec![]
527 }
528
529 fn nop(&mut self) -> Vec<CoProcessorCall> {
530 self.instruction_pointer += 1;
531 vec![]
532 }
533
534 fn skiz(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
535 let top_of_stack = self.op_stack.pop()?;
536 self.instruction_pointer += match top_of_stack.is_zero() {
537 true => 1 + self.next_instruction()?.size(),
538 false => 1,
539 };
540 Ok(vec![])
541 }
542
543 fn call(&mut self, call_destination: BFieldElement) -> Vec<CoProcessorCall> {
544 let size_of_instruction_call = 2;
545 let call_origin = (self.instruction_pointer as u32 + size_of_instruction_call).into();
546 let jump_stack_entry = (call_origin, call_destination);
547 self.jump_stack.push(jump_stack_entry);
548
549 self.instruction_pointer = call_destination.value().try_into().unwrap();
550 vec![]
551 }
552
553 fn return_from_call(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
554 let (call_origin, _) = self.jump_stack_pop()?;
555 self.instruction_pointer = call_origin.value().try_into().unwrap();
556 Ok(vec![])
557 }
558
559 fn recurse(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
560 let (_, call_destination) = self.jump_stack_peek()?;
561 self.instruction_pointer = call_destination.value().try_into().unwrap();
562 Ok(vec![])
563 }
564
565 fn recurse_or_return(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
566 if self.jump_stack.is_empty() {
567 return Err(InstructionError::JumpStackIsEmpty);
568 }
569
570 let new_ip = if self.op_stack[5] == self.op_stack[6] {
571 let (call_origin, _) = self.jump_stack_pop()?;
572 call_origin
573 } else {
574 let (_, call_destination) = self.jump_stack_peek()?;
575 call_destination
576 };
577
578 self.instruction_pointer = new_ip.value().try_into().unwrap();
579
580 Ok(vec![])
581 }
582
583 fn assert(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
584 let actual = self.op_stack[0];
585 let expected = BFieldElement::ONE;
586 if actual != expected {
587 let error = self.contextualized_assertion_error(expected, actual);
588 return Err(InstructionError::AssertionFailed(error));
589 }
590 let _ = self.op_stack.pop()?;
591
592 self.instruction_pointer += 1;
593 Ok(vec![])
594 }
595
596 fn halt(&mut self) -> Vec<CoProcessorCall> {
597 self.halting = true;
598 self.instruction_pointer += 1;
599 vec![]
600 }
601
602 fn read_mem(&mut self, n: NumberOfWords) -> InstructionResult<Vec<CoProcessorCall>> {
603 self.start_recording_ram_calls();
604 let mut ram_pointer = self.op_stack.pop()?;
605 for _ in 0..n.num_words() {
606 let ram_value = self.ram_read(ram_pointer);
607 self.op_stack.push(ram_value);
608 ram_pointer.decrement();
609 }
610 self.op_stack.push(ram_pointer);
611 let ram_calls = self.stop_recording_ram_calls();
612
613 self.instruction_pointer += 2;
614 Ok(ram_calls)
615 }
616
617 fn write_mem(&mut self, n: NumberOfWords) -> InstructionResult<Vec<CoProcessorCall>> {
618 self.start_recording_ram_calls();
619 let mut ram_pointer = self.op_stack.pop()?;
620 for _ in 0..n.num_words() {
621 let ram_value = self.op_stack.pop()?;
622 self.ram_write(ram_pointer, ram_value);
623 ram_pointer.increment();
624 }
625 self.op_stack.push(ram_pointer);
626 let ram_calls = self.stop_recording_ram_calls();
627
628 self.instruction_pointer += 2;
629 Ok(ram_calls)
630 }
631
632 fn ram_read(&mut self, ram_pointer: BFieldElement) -> BFieldElement {
633 let ram_value = self
634 .ram
635 .get(&ram_pointer)
636 .copied()
637 .unwrap_or(BFieldElement::ZERO);
638
639 let ram_table_call = RamTableCall {
640 clk: self.cycle_count,
641 ram_pointer,
642 ram_value,
643 is_write: false,
644 };
645 self.ram_calls.push(ram_table_call);
646
647 ram_value
648 }
649
650 fn ram_write(&mut self, ram_pointer: BFieldElement, ram_value: BFieldElement) {
651 let ram_table_call = RamTableCall {
652 clk: self.cycle_count,
653 ram_pointer,
654 ram_value,
655 is_write: true,
656 };
657 self.ram_calls.push(ram_table_call);
658
659 self.ram.insert(ram_pointer, ram_value);
660 }
661
662 fn hash(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
663 let to_hash = self.op_stack.pop_multiple::<{ tip5::RATE }>()?;
664
665 let mut hash_input = Tip5::new(sponge::Domain::FixedLength);
666 hash_input.state[..tip5::RATE].copy_from_slice(&to_hash);
667 let tip5_trace = hash_input.trace();
668 let hash_output = &tip5_trace[tip5_trace.len() - 1][0..Digest::LEN];
669
670 for i in (0..Digest::LEN).rev() {
671 self.op_stack.push(hash_output[i]);
672 }
673
674 let co_processor_calls = vec![CoProcessorCall::Tip5Trace(
675 Instruction::Hash,
676 Box::new(tip5_trace),
677 )];
678
679 self.instruction_pointer += 1;
680 Ok(co_processor_calls)
681 }
682
683 fn sponge_init(&mut self) -> Vec<CoProcessorCall> {
684 self.sponge = Some(Tip5::init());
685 self.instruction_pointer += 1;
686 vec![CoProcessorCall::SpongeStateReset]
687 }
688
689 fn sponge_absorb(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
690 let Some(ref mut sponge) = self.sponge else {
691 return Err(InstructionError::SpongeNotInitialized);
692 };
693 let to_absorb = self.op_stack.pop_multiple::<{ tip5::RATE }>()?;
694 sponge.state[..tip5::RATE].copy_from_slice(&to_absorb);
695 let tip5_trace = sponge.trace();
696
697 let co_processor_calls = vec![CoProcessorCall::Tip5Trace(
698 Instruction::SpongeAbsorb,
699 Box::new(tip5_trace),
700 )];
701
702 self.instruction_pointer += 1;
703 Ok(co_processor_calls)
704 }
705
706 fn sponge_absorb_mem(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
707 let Some(mut sponge) = self.sponge.take() else {
708 return Err(InstructionError::SpongeNotInitialized);
709 };
710
711 self.start_recording_ram_calls();
712 let mut mem_pointer = self.op_stack.pop()?;
713 for i in 0..tip5::RATE {
714 let element = self.ram_read(mem_pointer);
715 mem_pointer.increment();
716 sponge.state[i] = element;
717
718 if i < tip5::RATE - NUM_HELPER_VARIABLE_REGISTERS {
720 self.op_stack[i] = element;
721 }
722 }
723 self.op_stack.push(mem_pointer);
724
725 let tip5_trace = sponge.trace();
726 self.sponge = Some(sponge);
727
728 let mut co_processor_calls = self.stop_recording_ram_calls();
729 co_processor_calls.push(CoProcessorCall::Tip5Trace(
730 Instruction::SpongeAbsorb,
731 Box::new(tip5_trace),
732 ));
733
734 self.instruction_pointer += 1;
735 Ok(co_processor_calls)
736 }
737
738 fn sponge_squeeze(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
739 let Some(ref mut sponge) = self.sponge else {
740 return Err(InstructionError::SpongeNotInitialized);
741 };
742 for i in (0..tip5::RATE).rev() {
743 self.op_stack.push(sponge.state[i]);
744 }
745 let tip5_trace = sponge.trace();
746
747 let co_processor_calls = vec![CoProcessorCall::Tip5Trace(
748 Instruction::SpongeSqueeze,
749 Box::new(tip5_trace),
750 )];
751
752 self.instruction_pointer += 1;
753 Ok(co_processor_calls)
754 }
755
756 fn assert_vector(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
757 for i in 0..Digest::LEN {
758 let expected = self.op_stack[i];
759 let actual = self.op_stack[i + Digest::LEN];
760 if expected != actual {
761 let error = self.contextualized_assertion_error(expected, actual);
762 return Err(InstructionError::VectorAssertionFailed(i, error));
763 }
764 }
765 self.op_stack.pop_multiple::<{ Digest::LEN }>()?;
766 self.instruction_pointer += 1;
767 Ok(vec![])
768 }
769
770 fn add(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
771 let lhs = self.op_stack.pop()?;
772 let rhs = self.op_stack.pop()?;
773 self.op_stack.push(lhs + rhs);
774
775 self.instruction_pointer += 1;
776 Ok(vec![])
777 }
778
779 fn addi(&mut self, i: BFieldElement) -> Vec<CoProcessorCall> {
780 self.op_stack[0] += i;
781 self.instruction_pointer += 2;
782 vec![]
783 }
784
785 fn mul(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
786 let lhs = self.op_stack.pop()?;
787 let rhs = self.op_stack.pop()?;
788 self.op_stack.push(lhs * rhs);
789
790 self.instruction_pointer += 1;
791 Ok(vec![])
792 }
793
794 fn invert(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
795 let top_of_stack = self.op_stack[0];
796 if top_of_stack.is_zero() {
797 return Err(InstructionError::InverseOfZero);
798 }
799 let _ = self.op_stack.pop()?;
800 self.op_stack.push(top_of_stack.inverse());
801 self.instruction_pointer += 1;
802 Ok(vec![])
803 }
804
805 fn eq(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
806 let lhs = self.op_stack.pop()?;
807 let rhs = self.op_stack.pop()?;
808 let eq: u32 = (lhs == rhs).into();
809 self.op_stack.push(eq.into());
810
811 self.instruction_pointer += 1;
812 Ok(vec![])
813 }
814
815 fn split(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
816 let top_of_stack = self.op_stack.pop()?;
817 let lo = bfe!(top_of_stack.value() & 0xffff_ffff);
818 let hi = bfe!(top_of_stack.value() >> 32);
819 self.op_stack.push(hi);
820 self.op_stack.push(lo);
821
822 let u32_table_entry = U32TableEntry::new(Instruction::Split, lo, hi);
823 let co_processor_calls = vec![CoProcessorCall::U32(u32_table_entry)];
824
825 self.instruction_pointer += 1;
826 Ok(co_processor_calls)
827 }
828
829 fn lt(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
830 self.op_stack.is_u32(OpStackElement::ST0)?;
831 self.op_stack.is_u32(OpStackElement::ST1)?;
832 let lhs = self.op_stack.pop_u32()?;
833 let rhs = self.op_stack.pop_u32()?;
834 let lt: u32 = (lhs < rhs).into();
835 self.op_stack.push(lt.into());
836
837 let u32_table_entry = U32TableEntry::new(Instruction::Lt, lhs, rhs);
838 let co_processor_calls = vec![CoProcessorCall::U32(u32_table_entry)];
839
840 self.instruction_pointer += 1;
841 Ok(co_processor_calls)
842 }
843
844 fn and(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
845 self.op_stack.is_u32(OpStackElement::ST0)?;
846 self.op_stack.is_u32(OpStackElement::ST1)?;
847 let lhs = self.op_stack.pop_u32()?;
848 let rhs = self.op_stack.pop_u32()?;
849 let and = lhs & rhs;
850 self.op_stack.push(and.into());
851
852 let u32_table_entry = U32TableEntry::new(Instruction::And, lhs, rhs);
853 let co_processor_calls = vec![CoProcessorCall::U32(u32_table_entry)];
854
855 self.instruction_pointer += 1;
856 Ok(co_processor_calls)
857 }
858
859 fn xor(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
860 self.op_stack.is_u32(OpStackElement::ST0)?;
861 self.op_stack.is_u32(OpStackElement::ST1)?;
862 let lhs = self.op_stack.pop_u32()?;
863 let rhs = self.op_stack.pop_u32()?;
864 let xor = lhs ^ rhs;
865 self.op_stack.push(xor.into());
866
867 let u32_table_entry = U32TableEntry::new(Instruction::And, lhs, rhs);
871 let co_processor_calls = vec![CoProcessorCall::U32(u32_table_entry)];
872
873 self.instruction_pointer += 1;
874 Ok(co_processor_calls)
875 }
876
877 fn log_2_floor(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
878 self.op_stack.is_u32(OpStackElement::ST0)?;
879 let top_of_stack = self.op_stack[0];
880 if top_of_stack.is_zero() {
881 return Err(InstructionError::LogarithmOfZero);
882 }
883 let top_of_stack = self.op_stack.pop_u32()?;
884 let log_2_floor = top_of_stack.ilog2();
885 self.op_stack.push(log_2_floor.into());
886
887 let u32_table_entry = U32TableEntry::new(Instruction::Log2Floor, top_of_stack, 0);
888 let co_processor_calls = vec![CoProcessorCall::U32(u32_table_entry)];
889
890 self.instruction_pointer += 1;
891 Ok(co_processor_calls)
892 }
893
894 fn pow(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
895 self.op_stack.is_u32(OpStackElement::ST1)?;
896 let base = self.op_stack.pop()?;
897 let exponent = self.op_stack.pop_u32()?;
898 let base_pow_exponent = base.mod_pow(exponent.into());
899 self.op_stack.push(base_pow_exponent);
900
901 let u32_table_entry = U32TableEntry::new(Instruction::Pow, base, exponent);
902 let co_processor_calls = vec![CoProcessorCall::U32(u32_table_entry)];
903
904 self.instruction_pointer += 1;
905 Ok(co_processor_calls)
906 }
907
908 fn div_mod(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
909 self.op_stack.is_u32(OpStackElement::ST0)?;
910 self.op_stack.is_u32(OpStackElement::ST1)?;
911 let denominator = self.op_stack[1];
912 if denominator.is_zero() {
913 return Err(InstructionError::DivisionByZero);
914 }
915
916 let numerator = self.op_stack.pop_u32()?;
917 let denominator = self.op_stack.pop_u32()?;
918 let quotient = numerator / denominator;
919 let remainder = numerator % denominator;
920
921 self.op_stack.push(quotient.into());
922 self.op_stack.push(remainder.into());
923
924 let remainder_is_less_than_denominator =
925 U32TableEntry::new(Instruction::Lt, remainder, denominator);
926 let numerator_and_quotient_range_check =
927 U32TableEntry::new(Instruction::Split, numerator, quotient);
928 let co_processor_calls = vec![
929 CoProcessorCall::U32(remainder_is_less_than_denominator),
930 CoProcessorCall::U32(numerator_and_quotient_range_check),
931 ];
932
933 self.instruction_pointer += 1;
934 Ok(co_processor_calls)
935 }
936
937 fn pop_count(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
938 self.op_stack.is_u32(OpStackElement::ST0)?;
939 let top_of_stack = self.op_stack.pop_u32()?;
940 let pop_count = top_of_stack.count_ones();
941 self.op_stack.push(pop_count.into());
942
943 let u32_table_entry = U32TableEntry::new(Instruction::PopCount, top_of_stack, 0);
944 let co_processor_calls = vec![CoProcessorCall::U32(u32_table_entry)];
945
946 self.instruction_pointer += 1;
947 Ok(co_processor_calls)
948 }
949
950 fn xx_add(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
951 let lhs = self.op_stack.pop_extension_field_element()?;
952 let rhs = self.op_stack.pop_extension_field_element()?;
953 self.op_stack.push_extension_field_element(lhs + rhs);
954 self.instruction_pointer += 1;
955 Ok(vec![])
956 }
957
958 fn xx_mul(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
959 let lhs = self.op_stack.pop_extension_field_element()?;
960 let rhs = self.op_stack.pop_extension_field_element()?;
961 self.op_stack.push_extension_field_element(lhs * rhs);
962 self.instruction_pointer += 1;
963 Ok(vec![])
964 }
965
966 fn x_invert(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
967 let top_of_stack = self.op_stack.peek_at_top_extension_field_element();
968 if top_of_stack.is_zero() {
969 return Err(InstructionError::InverseOfZero);
970 }
971 let inverse = top_of_stack.inverse();
972 let _ = self.op_stack.pop_extension_field_element()?;
973 self.op_stack.push_extension_field_element(inverse);
974 self.instruction_pointer += 1;
975 Ok(vec![])
976 }
977
978 fn xb_mul(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
979 let lhs = self.op_stack.pop()?;
980 let rhs = self.op_stack.pop_extension_field_element()?;
981 self.op_stack.push_extension_field_element(lhs.lift() * rhs);
982
983 self.instruction_pointer += 1;
984 Ok(vec![])
985 }
986
987 fn write_io(&mut self, n: NumberOfWords) -> InstructionResult<Vec<CoProcessorCall>> {
988 for _ in 0..n.num_words() {
989 let top_of_stack = self.op_stack.pop()?;
990 self.public_output.push(top_of_stack);
991 }
992
993 self.instruction_pointer += 2;
994 Ok(vec![])
995 }
996
997 fn read_io(&mut self, n: NumberOfWords) -> InstructionResult<Vec<CoProcessorCall>> {
998 let input_len = self.public_input.len();
999 if input_len < n.num_words() {
1000 return Err(InstructionError::EmptyPublicInput(input_len));
1001 }
1002 for _ in 0..n.num_words() {
1003 let read_element = self.public_input.pop_front().unwrap();
1004 self.op_stack.push(read_element);
1005 }
1006
1007 self.instruction_pointer += 2;
1008 Ok(vec![])
1009 }
1010
1011 fn merkle_step_non_determinism(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
1012 self.op_stack.is_u32(OpStackElement::ST5)?;
1013 let sibling_digest = self.pop_secret_digest()?;
1014 self.merkle_step(sibling_digest)
1015 }
1016
1017 fn merkle_step_mem(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
1018 self.op_stack.is_u32(OpStackElement::ST5)?;
1019 self.start_recording_ram_calls();
1020 let mut ram_pointer = self.op_stack[7];
1021 let Digest(mut sibling_digest) = Digest::default();
1022 for digest_element in &mut sibling_digest {
1023 *digest_element = self.ram_read(ram_pointer);
1024 ram_pointer.increment();
1025 }
1026 self.op_stack[7] = ram_pointer;
1027
1028 let mut co_processor_calls = self.merkle_step(sibling_digest)?;
1029 co_processor_calls.extend(self.stop_recording_ram_calls());
1030 Ok(co_processor_calls)
1031 }
1032
1033 fn merkle_step(
1034 &mut self,
1035 sibling_digest: [BFieldElement; Digest::LEN],
1036 ) -> InstructionResult<Vec<CoProcessorCall>> {
1037 let node_index = self.op_stack.get_u32(OpStackElement::ST5)?;
1038 let parent_node_index = node_index / 2;
1039
1040 let accumulator_digest = self.op_stack.pop_multiple::<{ Digest::LEN }>()?;
1041 let (left_sibling, right_sibling) = match node_index % 2 {
1042 0 => (accumulator_digest, sibling_digest),
1043 1 => (sibling_digest, accumulator_digest),
1044 _ => unreachable!(),
1045 };
1046
1047 let mut tip5 = Tip5::new(sponge::Domain::FixedLength);
1048 tip5.state[..Digest::LEN].copy_from_slice(&left_sibling);
1049 tip5.state[Digest::LEN..2 * Digest::LEN].copy_from_slice(&right_sibling);
1050 let tip5_trace = tip5.trace();
1051 let accumulator_digest = &tip5_trace.last().unwrap()[0..Digest::LEN];
1052
1053 for &digest_element in accumulator_digest.iter().rev() {
1054 self.op_stack.push(digest_element);
1055 }
1056 self.op_stack[5] = parent_node_index.into();
1057
1058 self.instruction_pointer += 1;
1059
1060 let hash_co_processor_call =
1061 CoProcessorCall::Tip5Trace(Instruction::Hash, Box::new(tip5_trace));
1062 let indices_are_u32 = CoProcessorCall::U32(U32TableEntry::new(
1063 Instruction::Split,
1064 node_index,
1065 parent_node_index,
1066 ));
1067 let co_processor_calls = vec![hash_co_processor_call, indices_are_u32];
1068 Ok(co_processor_calls)
1069 }
1070
1071 fn xx_dot_step(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
1072 self.start_recording_ram_calls();
1073 let mut rhs_address = self.op_stack.pop()?;
1074 let mut lhs_address = self.op_stack.pop()?;
1075 let mut rhs = xfe!(0);
1076 let mut lhs = xfe!(0);
1077 for i in 0..EXTENSION_DEGREE {
1078 rhs.coefficients[i] = self.ram_read(rhs_address);
1079 rhs_address.increment();
1080 lhs.coefficients[i] = self.ram_read(lhs_address);
1081 lhs_address.increment();
1082 }
1083 let accumulator = self.op_stack.pop_extension_field_element()? + rhs * lhs;
1084 self.op_stack.push_extension_field_element(accumulator);
1085 self.op_stack.push(lhs_address);
1086 self.op_stack.push(rhs_address);
1087 self.instruction_pointer += 1;
1088 let ram_calls = self.stop_recording_ram_calls();
1089 Ok(ram_calls)
1090 }
1091
1092 fn xb_dot_step(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
1093 self.start_recording_ram_calls();
1094 let mut rhs_address = self.op_stack.pop()?;
1095 let mut lhs_address = self.op_stack.pop()?;
1096 let rhs = self.ram_read(rhs_address);
1097 rhs_address.increment();
1098 let mut lhs = xfe!(0);
1099 for i in 0..EXTENSION_DEGREE {
1100 lhs.coefficients[i] = self.ram_read(lhs_address);
1101 lhs_address.increment();
1102 }
1103 let accumulator = self.op_stack.pop_extension_field_element()? + rhs * lhs;
1104 self.op_stack.push_extension_field_element(accumulator);
1105 self.op_stack.push(lhs_address);
1106 self.op_stack.push(rhs_address);
1107 self.instruction_pointer += 1;
1108 let ram_calls = self.stop_recording_ram_calls();
1109 Ok(ram_calls)
1110 }
1111
1112 pub fn to_processor_row(&self) -> Array1<BFieldElement> {
1113 use ProcessorMainColumn as Col;
1114 use isa::instruction::InstructionBit;
1115
1116 assert!(
1117 self.op_stack.len() >= OpStackElement::COUNT,
1118 "unrecoverable internal error: Triton VM's stack is too shallow",
1119 );
1120
1121 let mut row = Array1::zeros(Col::COUNT);
1122 row[Col::CLK.main_index()] = u64::from(self.cycle_count).into();
1123 row[Col::IP.main_index()] = (self.instruction_pointer as u32).into();
1124
1125 let current_instruction = self.current_instruction().unwrap_or(Instruction::Nop);
1126 row[Col::CI.main_index()] = current_instruction.opcode_b();
1127 row[Col::NIA.main_index()] = self.next_instruction_or_argument();
1128 row[Col::IB0.main_index()] = current_instruction.ib(InstructionBit::IB0);
1129 row[Col::IB1.main_index()] = current_instruction.ib(InstructionBit::IB1);
1130 row[Col::IB2.main_index()] = current_instruction.ib(InstructionBit::IB2);
1131 row[Col::IB3.main_index()] = current_instruction.ib(InstructionBit::IB3);
1132 row[Col::IB4.main_index()] = current_instruction.ib(InstructionBit::IB4);
1133 row[Col::IB5.main_index()] = current_instruction.ib(InstructionBit::IB5);
1134 row[Col::IB6.main_index()] = current_instruction.ib(InstructionBit::IB6);
1135
1136 row[Col::JSP.main_index()] = self.jump_stack_pointer();
1137 row[Col::JSO.main_index()] = self.jump_stack_origin();
1138 row[Col::JSD.main_index()] = self.jump_stack_destination();
1139 row[Col::ST0.main_index()] = self.op_stack[0];
1140 row[Col::ST1.main_index()] = self.op_stack[1];
1141 row[Col::ST2.main_index()] = self.op_stack[2];
1142 row[Col::ST3.main_index()] = self.op_stack[3];
1143 row[Col::ST4.main_index()] = self.op_stack[4];
1144 row[Col::ST5.main_index()] = self.op_stack[5];
1145 row[Col::ST6.main_index()] = self.op_stack[6];
1146 row[Col::ST7.main_index()] = self.op_stack[7];
1147 row[Col::ST8.main_index()] = self.op_stack[8];
1148 row[Col::ST9.main_index()] = self.op_stack[9];
1149 row[Col::ST10.main_index()] = self.op_stack[10];
1150 row[Col::ST11.main_index()] = self.op_stack[11];
1151 row[Col::ST12.main_index()] = self.op_stack[12];
1152 row[Col::ST13.main_index()] = self.op_stack[13];
1153 row[Col::ST14.main_index()] = self.op_stack[14];
1154 row[Col::ST15.main_index()] = self.op_stack[15];
1155 row[Col::OpStackPointer.main_index()] = self.op_stack.pointer();
1156
1157 let helper_variables = self.derive_helper_variables();
1158 row[Col::HV0.main_index()] = helper_variables[0];
1159 row[Col::HV1.main_index()] = helper_variables[1];
1160 row[Col::HV2.main_index()] = helper_variables[2];
1161 row[Col::HV3.main_index()] = helper_variables[3];
1162 row[Col::HV4.main_index()] = helper_variables[4];
1163 row[Col::HV5.main_index()] = helper_variables[5];
1164
1165 row
1166 }
1167
1168 fn next_instruction_or_argument(&self) -> BFieldElement {
1178 let Ok(current_instruction) = self.current_instruction() else {
1179 return bfe!(0);
1180 };
1181 if let Some(argument) = current_instruction.arg() {
1182 return argument;
1183 }
1184 match self.next_instruction() {
1185 Ok(next_instruction) => next_instruction.opcode_b(),
1186 Err(_) => bfe!(1),
1187 }
1188 }
1189
1190 fn jump_stack_pointer(&self) -> BFieldElement {
1191 (self.jump_stack.len() as u64).into()
1192 }
1193
1194 fn jump_stack_origin(&self) -> BFieldElement {
1195 let maybe_origin = self.jump_stack.last().map(|(o, _d)| *o);
1196 maybe_origin.unwrap_or_else(BFieldElement::zero)
1197 }
1198
1199 fn jump_stack_destination(&self) -> BFieldElement {
1200 let maybe_destination = self.jump_stack.last().map(|(_o, d)| *d);
1201 maybe_destination.unwrap_or_else(BFieldElement::zero)
1202 }
1203
1204 pub fn current_instruction(&self) -> InstructionResult<Instruction> {
1205 let instructions = &self.program.instructions;
1206 let maybe_current_instruction = instructions.get(self.instruction_pointer).copied();
1207 maybe_current_instruction.ok_or(InstructionError::InstructionPointerOverflow)
1208 }
1209
1210 pub fn next_instruction(&self) -> InstructionResult<Instruction> {
1217 let current_instruction = self.current_instruction()?;
1218 let next_instruction_pointer = self.instruction_pointer + current_instruction.size();
1219 let instructions = &self.program.instructions;
1220 let maybe_next_instruction = instructions.get(next_instruction_pointer).copied();
1221 maybe_next_instruction.ok_or(InstructionError::InstructionPointerOverflow)
1222 }
1223
1224 fn jump_stack_pop(&mut self) -> InstructionResult<(BFieldElement, BFieldElement)> {
1225 self.jump_stack
1226 .pop()
1227 .ok_or(InstructionError::JumpStackIsEmpty)
1228 }
1229
1230 fn jump_stack_peek(&mut self) -> InstructionResult<(BFieldElement, BFieldElement)> {
1231 self.jump_stack
1232 .last()
1233 .copied()
1234 .ok_or(InstructionError::JumpStackIsEmpty)
1235 }
1236
1237 fn pop_secret_digest(&mut self) -> InstructionResult<[BFieldElement; Digest::LEN]> {
1238 let digest = self
1239 .secret_digests
1240 .pop_front()
1241 .ok_or(InstructionError::EmptySecretDigestInput)?;
1242 Ok(digest.values())
1243 }
1244
1245 pub fn run(&mut self) -> InstructionResult<()> {
1247 while !self.halting {
1248 self.step()?;
1249 }
1250 Ok(())
1251 }
1252
1253 fn contextualized_assertion_error(
1254 &self,
1255 expected: BFieldElement,
1256 actual: BFieldElement,
1257 ) -> AssertionError {
1258 let current_address =
1259 u64::try_from(self.instruction_pointer).expect("usize should fit in u64");
1260
1261 let error = AssertionError::new(expected, actual);
1262 if let Some(context) = self.program.assertion_context_at(current_address) {
1263 error.with_context(context)
1264 } else {
1265 error
1266 }
1267 }
1268}
1269
1270impl Display for VMState {
1271 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
1272 use ProcessorMainColumn as ProcCol;
1273
1274 let display_instruction = |instruction: Instruction| {
1275 if let Instruction::Call(address) = instruction {
1276 format!("call {}", self.program.label_for_address(address.value()))
1277 } else {
1278 instruction.to_string()
1279 }
1280 };
1281
1282 let instruction = self
1283 .current_instruction()
1284 .map_or_else(|_| "--".to_string(), display_instruction)
1285 .chars()
1286 .take(54)
1287 .collect::<String>();
1288
1289 let total_width = 103;
1290 let tab_width = instruction.chars().count().max(8);
1291 let clk_width = 17;
1292 let register_width = 20;
1293 let buffer_width = total_width - tab_width - clk_width - 7;
1294
1295 let print_row = |f: &mut Formatter, s: String| writeln!(f, "│ {s: <total_width$} │");
1296 let print_blank_row = |f: &mut Formatter| print_row(f, String::new());
1297 let print_section_separator = |f: &mut Formatter| writeln!(f, "├─{:─<total_width$}─┤", "");
1298
1299 let row = self.to_processor_row();
1300
1301 let register =
1302 |col: ProcCol| format!("{:>register_width$}", row[col.main_index()].to_string());
1303 let multi_register = |regs: [_; 4]| regs.map(register).join(" | ");
1304
1305 writeln!(f)?;
1306 writeln!(f, " ╭─{:─<tab_width$}─╮", "")?;
1307 writeln!(f, " │ {instruction: <tab_width$} │")?;
1308 writeln!(
1309 f,
1310 "╭┴─{:─<tab_width$}─┴─{:─<buffer_width$}─┬─{:─>clk_width$}─╮",
1311 "", "", ""
1312 )?;
1313
1314 let ip = register(ProcCol::IP);
1315 let ci = register(ProcCol::CI);
1316 let nia = register(ProcCol::NIA);
1317 let jsp = register(ProcCol::JSP);
1318 let jso = register(ProcCol::JSO);
1319 let jsd = register(ProcCol::JSD);
1320 let osp = register(ProcCol::OpStackPointer);
1321 let clk = row[ProcCol::CLK.main_index()].to_string();
1322 let clk = clk.trim_start_matches('0');
1323
1324 let first_line = format!("ip: {ip} ╷ ci: {ci} ╷ nia: {nia} │ {clk: >clk_width$}");
1325 print_row(f, first_line)?;
1326 writeln!(
1327 f,
1328 "│ jsp: {jsp} │ jso: {jso} │ jsd: {jsd} ╰─{:─>clk_width$}─┤",
1329 "",
1330 )?;
1331 print_row(f, format!("osp: {osp} ╵"))?;
1332 print_blank_row(f)?;
1333
1334 let st_00_03 = multi_register([ProcCol::ST0, ProcCol::ST1, ProcCol::ST2, ProcCol::ST3]);
1335 let st_04_07 = multi_register([ProcCol::ST4, ProcCol::ST5, ProcCol::ST6, ProcCol::ST7]);
1336 let st_08_11 = multi_register([ProcCol::ST8, ProcCol::ST9, ProcCol::ST10, ProcCol::ST11]);
1337 let st_12_15 = multi_register([ProcCol::ST12, ProcCol::ST13, ProcCol::ST14, ProcCol::ST15]);
1338
1339 print_row(f, format!("st0-3: [ {st_00_03} ]"))?;
1340 print_row(f, format!("st4-7: [ {st_04_07} ]"))?;
1341 print_row(f, format!("st8-11: [ {st_08_11} ]"))?;
1342 print_row(f, format!("st12-15: [ {st_12_15} ]"))?;
1343 print_blank_row(f)?;
1344
1345 let hv_00_03_line = format!(
1346 "hv0-3: [ {} ]",
1347 multi_register([ProcCol::HV0, ProcCol::HV1, ProcCol::HV2, ProcCol::HV3])
1348 );
1349 let hv_04_05_line = format!(
1350 "hv4-5: [ {} | {} ]",
1351 register(ProcCol::HV4),
1352 register(ProcCol::HV5),
1353 );
1354 print_row(f, hv_00_03_line)?;
1355 print_row(f, hv_04_05_line)?;
1356
1357 let ib_registers = [
1358 ProcCol::IB6,
1359 ProcCol::IB5,
1360 ProcCol::IB4,
1361 ProcCol::IB3,
1362 ProcCol::IB2,
1363 ProcCol::IB1,
1364 ProcCol::IB0,
1365 ]
1366 .map(|reg| row[reg.main_index()])
1367 .map(|bfe| format!("{bfe:>2}"))
1368 .join(" | ");
1369 print_row(f, format!("ib6-0: [ {ib_registers} ]"))?;
1370
1371 print_section_separator(f)?;
1372 if let Some(ref sponge) = self.sponge {
1373 let sponge_state_slice = |idxs: Range<usize>| {
1374 idxs.map(|i| sponge.state[i].value())
1375 .map(|ss| format!("{ss:>register_width$}"))
1376 .join(" | ")
1377 };
1378
1379 print_row(f, format!("sp0-3: [ {} ]", sponge_state_slice(0..4)))?;
1380 print_row(f, format!("sp4-7: [ {} ]", sponge_state_slice(4..8)))?;
1381 print_row(f, format!("sp8-11: [ {} ]", sponge_state_slice(8..12)))?;
1382 print_row(f, format!("sp12-15: [ {} ]", sponge_state_slice(12..16)))?;
1383 } else {
1384 let uninit_msg = format!("{:^total_width$}", "-- sponge is not initialized --");
1385 print_row(f, uninit_msg)?;
1386 };
1387
1388 print_section_separator(f)?;
1389 if self.jump_stack.is_empty() {
1390 print_row(f, format!("{:^total_width$}", "-- jump stack is empty --"))?;
1391 } else {
1392 let idx_width = 3;
1393 let max_label_width = total_width - idx_width - 2; for (idx, &(_, address)) in self.jump_stack.iter().rev().enumerate() {
1396 let label = self.program.label_for_address(address.value());
1397 let label = label.chars().take(max_label_width).collect::<String>();
1398 print_row(f, format!("{idx:>idx_width$}: {label}"))?;
1399 print_row(f, format!(" at {address}"))?;
1400 }
1401 }
1402
1403 if self.halting {
1404 print_section_separator(f)?;
1405 print_row(f, format!("{:^total_width$}", "! halting !"))?;
1406 }
1407
1408 writeln!(f, "╰─{:─<total_width$}─╯", "")
1409 }
1410}
1411
1412#[derive(Debug, Default, Clone, Eq, PartialEq, BFieldCodec, Arbitrary)]
1413pub struct PublicInput {
1414 pub individual_tokens: Vec<BFieldElement>,
1415}
1416
1417impl From<Vec<BFieldElement>> for PublicInput {
1418 fn from(individual_tokens: Vec<BFieldElement>) -> Self {
1419 Self::new(individual_tokens)
1420 }
1421}
1422
1423impl From<PublicInput> for Vec<BFieldElement> {
1424 fn from(value: PublicInput) -> Self {
1425 value.individual_tokens
1426 }
1427}
1428
1429impl From<&Vec<BFieldElement>> for PublicInput {
1430 fn from(tokens: &Vec<BFieldElement>) -> Self {
1431 Self::new(tokens.to_owned())
1432 }
1433}
1434
1435impl<const N: usize> From<[BFieldElement; N]> for PublicInput {
1436 fn from(tokens: [BFieldElement; N]) -> Self {
1437 Self::new(tokens.to_vec())
1438 }
1439}
1440
1441impl From<&[BFieldElement]> for PublicInput {
1442 fn from(tokens: &[BFieldElement]) -> Self {
1443 Self::new(tokens.to_vec())
1444 }
1445}
1446
1447impl Deref for PublicInput {
1448 type Target = [BFieldElement];
1449
1450 fn deref(&self) -> &Self::Target {
1451 &self.individual_tokens
1452 }
1453}
1454
1455impl PublicInput {
1456 pub fn new(individual_tokens: Vec<BFieldElement>) -> Self {
1457 Self { individual_tokens }
1458 }
1459}
1460
1461#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize, Arbitrary)]
1465pub struct NonDeterminism {
1466 pub individual_tokens: Vec<BFieldElement>,
1467 pub digests: Vec<Digest>,
1468 pub ram: HashMap<BFieldElement, BFieldElement>,
1469}
1470
1471impl From<Vec<BFieldElement>> for NonDeterminism {
1472 fn from(tokens: Vec<BFieldElement>) -> Self {
1473 Self::new(tokens)
1474 }
1475}
1476
1477impl From<&Vec<BFieldElement>> for NonDeterminism {
1478 fn from(tokens: &Vec<BFieldElement>) -> Self {
1479 Self::new(tokens.to_owned())
1480 }
1481}
1482
1483impl<const N: usize> From<[BFieldElement; N]> for NonDeterminism {
1484 fn from(tokens: [BFieldElement; N]) -> Self {
1485 Self::new(tokens.to_vec())
1486 }
1487}
1488
1489impl From<&[BFieldElement]> for NonDeterminism {
1490 fn from(tokens: &[BFieldElement]) -> Self {
1491 Self::new(tokens.to_vec())
1492 }
1493}
1494
1495impl NonDeterminism {
1496 pub fn new<V: Into<Vec<BFieldElement>>>(individual_tokens: V) -> Self {
1497 Self {
1498 individual_tokens: individual_tokens.into(),
1499 digests: vec![],
1500 ram: HashMap::new(),
1501 }
1502 }
1503
1504 #[must_use]
1505 pub fn with_digests<V: Into<Vec<Digest>>>(mut self, digests: V) -> Self {
1506 self.digests = digests.into();
1507 self
1508 }
1509
1510 #[must_use]
1511 pub fn with_ram<H: Into<HashMap<BFieldElement, BFieldElement>>>(mut self, ram: H) -> Self {
1512 self.ram = ram.into();
1513 self
1514 }
1515}
1516
1517#[cfg(test)]
1518#[cfg_attr(coverage_nightly, coverage(off))]
1519pub(crate) mod tests {
1520 use std::ops::BitAnd;
1521 use std::ops::BitXor;
1522
1523 use assert2::assert;
1524 use assert2::let_assert;
1525 use isa::instruction::ALL_INSTRUCTIONS;
1526 use isa::instruction::AnInstruction;
1527 use isa::instruction::LabelledInstruction;
1528 use isa::op_stack::NUM_OP_STACK_REGISTERS;
1529 use isa::program::Program;
1530 use isa::triton_asm;
1531 use isa::triton_instr;
1532 use isa::triton_program;
1533 use itertools::izip;
1534 use proptest::collection::vec;
1535 use proptest::prelude::*;
1536 use proptest_arbitrary_interop::arb;
1537 use rand::Rng;
1538 use rand::RngCore;
1539 use rand::rngs::ThreadRng;
1540 use strum::EnumCount;
1541 use strum::EnumIter;
1542 use test_strategy::proptest;
1543 use twenty_first::math::other::random_elements;
1544
1545 use crate::shared_tests::LeavedMerkleTreeTestData;
1546 use crate::shared_tests::TestableProgram;
1547 use crate::stark::Stark;
1548 use crate::stark::tests::program_executing_every_instruction;
1549
1550 use super::*;
1551
1552 #[test]
1553 fn instructions_act_on_op_stack_as_indicated() {
1554 for test_instruction in ALL_INSTRUCTIONS {
1555 let (program, stack_size_before_test_instruction) =
1556 construct_test_program_for_instruction(test_instruction);
1557 let public_input = PublicInput::from(bfe_array![0]);
1558 let mock_digests = [Digest::default()];
1559 let non_determinism = NonDeterminism::from(bfe_array![0]).with_digests(mock_digests);
1560
1561 let mut vm_state = VMState::new(program, public_input, non_determinism);
1562 let_assert!(Ok(()) = vm_state.run());
1563 let stack_size_after_test_instruction = vm_state.op_stack.len();
1564
1565 let stack_size_difference = (stack_size_after_test_instruction as i32)
1566 - (stack_size_before_test_instruction as i32);
1567 assert!(
1568 test_instruction.op_stack_size_influence() == stack_size_difference,
1569 "{test_instruction}"
1570 );
1571 }
1572 }
1573
1574 fn construct_test_program_for_instruction(
1575 instruction: AnInstruction<BFieldElement>,
1576 ) -> (Program, usize) {
1577 if matches!(
1578 instruction,
1579 Instruction::Call(_)
1580 | Instruction::Return
1581 | Instruction::Recurse
1582 | Instruction::RecurseOrReturn
1583 ) {
1584 let program = test_program_for_call_recurse_return().program;
1586 let stack_size = NUM_OP_STACK_REGISTERS;
1587 (program, stack_size)
1588 } else {
1589 let num_push_instructions = 10;
1590 let push_instructions = triton_asm![push 1; num_push_instructions];
1591 let program = triton_program!(sponge_init {&push_instructions} {instruction} nop halt);
1592
1593 let stack_size_when_reaching_test_instruction =
1594 NUM_OP_STACK_REGISTERS + num_push_instructions;
1595 (program, stack_size_when_reaching_test_instruction)
1596 }
1597 }
1598
1599 #[proptest]
1600 fn from_various_types_to_public_input(#[strategy(arb())] tokens: Vec<BFieldElement>) {
1601 let public_input = PublicInput::new(tokens.clone());
1602
1603 assert!(public_input == tokens.clone().into());
1604 assert!(public_input == (&tokens).into());
1605 assert!(public_input == tokens[..].into());
1606 assert!(public_input == (&tokens[..]).into());
1607 assert!(tokens == <Vec<_>>::from(public_input));
1608
1609 assert!(PublicInput::new(vec![]) == [].into());
1610 }
1611
1612 #[proptest]
1613 fn from_various_types_to_non_determinism(#[strategy(arb())] tokens: Vec<BFieldElement>) {
1614 let non_determinism = NonDeterminism::new(tokens.clone());
1615
1616 assert!(non_determinism == tokens.clone().into());
1617 assert!(non_determinism == tokens[..].into());
1618 assert!(non_determinism == (&tokens[..]).into());
1619
1620 assert!(NonDeterminism::new(vec![]) == [].into());
1621 }
1622
1623 #[test]
1624 fn initialise_table() {
1625 let program = crate::example_programs::GREATEST_COMMON_DIVISOR.clone();
1626 let stdin = PublicInput::from([42, 56].map(|b| bfe!(b)));
1627 let secret_in = NonDeterminism::default();
1628 VM::trace_execution(program, stdin, secret_in).unwrap();
1629 }
1630
1631 #[test]
1632 fn run_tvm_gcd() {
1633 let program = crate::example_programs::GREATEST_COMMON_DIVISOR.clone();
1634 let stdin = PublicInput::from([42, 56].map(|b| bfe!(b)));
1635 let secret_in = NonDeterminism::default();
1636 let_assert!(Ok(stdout) = VM::run(program, stdin, secret_in));
1637
1638 let output = stdout.iter().map(|o| format!("{o}")).join(", ");
1639 println!("VM output: [{output}]");
1640
1641 assert!(bfe!(14) == stdout[0]);
1642 }
1643
1644 #[test]
1645 fn crash_triton_vm_and_print_vm_error() {
1646 let crashing_program = triton_program!(push 2 assert halt);
1647 let_assert!(Err(err) = VM::run(crashing_program, [].into(), [].into()));
1648 println!("{err}");
1649 }
1650
1651 #[test]
1652 fn crash_triton_vm_with_non_empty_jump_stack_and_print_vm_error() {
1653 let crashing_program = triton_program! {
1654 call foo halt
1655 foo: call bar return
1656 bar: push 2 assert return
1657 };
1658 let_assert!(Err(err) = VM::run(crashing_program, [].into(), [].into()));
1659 let err_str = err.to_string();
1660
1661 let_assert!(Some(bar_pos) = err_str.find("bar"));
1662 let_assert!(Some(foo_pos) = err_str.find("foo"));
1663 assert!(bar_pos < foo_pos, "deeper call must be listed higher");
1664
1665 println!("{err_str}");
1666 }
1667
1668 #[test]
1669 fn print_various_vm_states() {
1670 let TestableProgram {
1671 program,
1672 public_input,
1673 non_determinism,
1674 ..
1675 } = program_executing_every_instruction();
1676 let mut state = VMState::new(program, public_input, non_determinism);
1677 while !state.halting {
1678 println!("{state}");
1679 state.step().unwrap();
1680 }
1681 }
1682
1683 #[test]
1684 fn print_vm_state_with_long_jump_stack() {
1685 let labels = [
1686 "astraldropper_",
1687 "bongoquest_",
1688 "cloudmother_",
1689 "daydream_",
1690 "essence_",
1691 "flowerflight_",
1692 "groovesister_",
1693 "highride_",
1694 "meadowdream_",
1695 "naturesounds_",
1696 "opaldancer_",
1697 "peacespirit_",
1698 "quyhrmfields_",
1699 ]
1700 .map(|s| s.repeat(15));
1701
1702 let crashing_program = triton_program! {
1703 call {labels[0]}
1704 {labels[0]}: call {labels[1]}
1705 {labels[1]}: call {labels[2]}
1706 {labels[2]}: call {labels[3]}
1707 {labels[3]}: call {labels[4]}
1708 {labels[4]}: call {labels[5]}
1709 {labels[5]}: call {labels[6]}
1710 {labels[6]}: call {labels[7]}
1711 {labels[7]}: call {labels[8]}
1712 {labels[8]}: call {labels[9]}
1713 {labels[9]}: call {labels[10]}
1714 {labels[10]}: call {labels[11]}
1715 {labels[11]}: call {labels[12]}
1716 {labels[12]}: nop
1717 };
1718
1719 let_assert!(Err(err) = VM::run(crashing_program, [].into(), [].into()));
1720 println!("{err}");
1721 }
1722
1723 pub(crate) fn test_program_hash_nop_nop_lt() -> TestableProgram {
1724 let push_5_zeros = triton_asm![push 0; 5];
1725 let program = triton_program! {
1726 {&push_5_zeros} hash
1727 nop
1728 {&push_5_zeros} hash
1729 nop nop
1730 {&push_5_zeros} hash
1731 push 3 push 2 lt assert
1732 halt
1733 };
1734 TestableProgram::new(program)
1735 }
1736
1737 pub(crate) fn test_program_for_halt() -> TestableProgram {
1738 TestableProgram::new(triton_program!(halt))
1739 }
1740
1741 pub(crate) fn test_program_for_push_pop_dup_swap_nop() -> TestableProgram {
1742 TestableProgram::new(triton_program!(
1743 push 1 push 2 pop 1 assert
1744 push 1 dup 0 assert assert
1745 push 1 push 2 swap 1 assert pop 1
1746 nop nop nop halt
1747 ))
1748 }
1749
1750 pub(crate) fn test_program_for_divine() -> TestableProgram {
1751 TestableProgram::new(triton_program!(divine 1 assert halt)).with_non_determinism([bfe!(1)])
1752 }
1753
1754 pub(crate) fn test_program_for_skiz() -> TestableProgram {
1755 TestableProgram::new(triton_program!(push 1 skiz push 0 skiz assert push 1 skiz halt))
1756 }
1757
1758 pub(crate) fn test_program_for_call_recurse_return() -> TestableProgram {
1759 TestableProgram::new(triton_program!(
1760 push 2
1761 call label
1762 pop 1 halt
1763 label:
1764 push -1 add dup 0
1765 skiz
1766 recurse
1767 return
1768 ))
1769 }
1770
1771 pub(crate) fn test_program_for_recurse_or_return() -> TestableProgram {
1772 TestableProgram::new(triton_program! {
1773 push 5 swap 5
1774 push 0 swap 5
1775 call label
1776 halt
1777 label:
1778 swap 5
1779 push 1 add
1780 swap 5
1781 recurse_or_return
1782 })
1783 }
1784
1785 #[derive(Debug, Clone, Eq, PartialEq, test_strategy::Arbitrary)]
1797 pub struct ProgramForRecurseOrReturn {
1798 #[strategy(arb())]
1799 iteration_terminator: BFieldElement,
1800
1801 #[strategy(arb())]
1802 #[filter(#other_iterator_values.iter().all(|&v| v != #iteration_terminator))]
1803 other_iterator_values: Vec<BFieldElement>,
1804 }
1805
1806 impl ProgramForRecurseOrReturn {
1807 pub fn assemble(self) -> TestableProgram {
1808 let expected_num_iterations = self.other_iterator_values.len() + 1;
1809
1810 let program = triton_program! {
1811 push 0 hint iteration_counter = stack[0]
1813
1814 push {self.iteration_terminator}
1816 swap 6
1817
1818 call iteration_loop
1819
1820 push {expected_num_iterations}
1822 eq assert
1823 halt
1824
1825 iteration_loop:
1826 push 1 add
1828
1829 swap 5
1831 pop 1
1832 read_io 1
1833 swap 5
1834 recurse_or_return
1835 };
1836
1837 let mut input = self.other_iterator_values;
1838 input.push(self.iteration_terminator);
1839 TestableProgram::new(program).with_input(input)
1840 }
1841 }
1842
1843 #[proptest]
1844 fn property_based_recurse_or_return_program_sanity_check(program: ProgramForRecurseOrReturn) {
1845 program.assemble().run()?;
1846 }
1847
1848 #[test]
1849 fn vm_crashes_when_executing_recurse_or_return_with_empty_jump_stack() {
1850 let program = triton_program!(recurse_or_return halt);
1851 let_assert!(Err(err) = VM::run(program, PublicInput::default(), NonDeterminism::default()));
1852 assert!(InstructionError::JumpStackIsEmpty == err.source);
1853 }
1854
1855 pub(crate) fn test_program_for_write_mem_read_mem() -> TestableProgram {
1856 TestableProgram::new(triton_program! {
1857 push 3 push 1 push 2 push 7 write_mem 3 push -1 add read_mem 2 pop 1 assert halt })
1865 }
1866
1867 pub(crate) fn test_program_for_hash() -> TestableProgram {
1868 let program = triton_program!(
1869 push 0 push 0 push 0 push 1 push 2 push 3
1871 hash
1872 read_io 1 eq assert halt
1873 );
1874 let hash_input = bfe_array![3, 2, 1, 0, 0, 0, 0, 0, 0, 0];
1875 let digest = Tip5::hash_10(&hash_input);
1876 TestableProgram::new(program).with_input(&digest[..=0])
1877 }
1878
1879 fn push_digest_to_stack_tasm(Digest([d0, d1, d2, d3, d4]): Digest) -> Vec<LabelledInstruction> {
1882 triton_asm!(push {d4} push {d3} push {d2} push {d1} push {d0})
1883 }
1884
1885 pub(crate) fn test_program_for_merkle_step_right_sibling() -> TestableProgram {
1886 let accumulator_digest = Digest::new(bfe_array![2, 12, 22, 32, 42]);
1887 let divined_digest = Digest::new(bfe_array![10, 11, 12, 13, 14]);
1888 let expected_digest = Tip5::hash_pair(divined_digest, accumulator_digest);
1889 let merkle_tree_node_index = 3;
1890 let program = triton_program!(
1891 push {merkle_tree_node_index}
1892 {&push_digest_to_stack_tasm(accumulator_digest)}
1893 merkle_step
1894
1895 {&push_digest_to_stack_tasm(expected_digest)}
1896 assert_vector pop 5
1897 assert halt
1898 );
1899
1900 let non_determinism = NonDeterminism::default().with_digests(vec![divined_digest]);
1901 TestableProgram::new(program).with_non_determinism(non_determinism)
1902 }
1903
1904 pub(crate) fn test_program_for_merkle_step_left_sibling() -> TestableProgram {
1905 let accumulator_digest = Digest::new(bfe_array![2, 12, 22, 32, 42]);
1906 let divined_digest = Digest::new(bfe_array![10, 11, 12, 13, 14]);
1907 let expected_digest = Tip5::hash_pair(accumulator_digest, divined_digest);
1908 let merkle_tree_node_index = 2;
1909 let program = triton_program!(
1910 push {merkle_tree_node_index}
1911 {&push_digest_to_stack_tasm(accumulator_digest)}
1912 merkle_step
1913
1914 {&push_digest_to_stack_tasm(expected_digest)}
1915 assert_vector pop 5
1916 assert halt
1917 );
1918
1919 let non_determinism = NonDeterminism::default().with_digests(vec![divined_digest]);
1920 TestableProgram::new(program).with_non_determinism(non_determinism)
1921 }
1922
1923 pub(crate) fn test_program_for_merkle_step_mem_right_sibling() -> TestableProgram {
1924 let accumulator_digest = Digest::new(bfe_array![2, 12, 22, 32, 42]);
1925 let sibling_digest = Digest::new(bfe_array![10, 11, 12, 13, 14]);
1926 let expected_digest = Tip5::hash_pair(sibling_digest, accumulator_digest);
1927 let auth_path_address = 1337;
1928 let merkle_tree_node_index = 3;
1929 let program = triton_program!(
1930 push {auth_path_address}
1931 push 0 push {merkle_tree_node_index}
1933 {&push_digest_to_stack_tasm(accumulator_digest)}
1934 merkle_step_mem
1935
1936 {&push_digest_to_stack_tasm(expected_digest)}
1937 assert_vector pop 5
1938 assert halt
1939 );
1940
1941 let ram = (auth_path_address..)
1942 .map(BFieldElement::new)
1943 .zip(sibling_digest.values())
1944 .collect::<HashMap<_, _>>();
1945 let non_determinism = NonDeterminism::default().with_ram(ram);
1946 TestableProgram::new(program).with_non_determinism(non_determinism)
1947 }
1948
1949 pub(crate) fn test_program_for_merkle_step_mem_left_sibling() -> TestableProgram {
1950 let accumulator_digest = Digest::new(bfe_array![2, 12, 22, 32, 42]);
1951 let sibling_digest = Digest::new(bfe_array![10, 11, 12, 13, 14]);
1952 let expected_digest = Tip5::hash_pair(accumulator_digest, sibling_digest);
1953 let auth_path_address = 1337;
1954 let merkle_tree_node_index = 2;
1955 let program = triton_program!(
1956 push {auth_path_address}
1957 push 0 push {merkle_tree_node_index}
1959 {&push_digest_to_stack_tasm(accumulator_digest)}
1960 merkle_step_mem
1961
1962 {&push_digest_to_stack_tasm(expected_digest)}
1963 assert_vector pop 5
1964 assert halt
1965 );
1966
1967 let ram = (auth_path_address..)
1968 .map(BFieldElement::new)
1969 .zip(sibling_digest.values())
1970 .collect::<HashMap<_, _>>();
1971 let non_determinism = NonDeterminism::default().with_ram(ram);
1972 TestableProgram::new(program).with_non_determinism(non_determinism)
1973 }
1974
1975 #[derive(Debug, Clone, test_strategy::Arbitrary)]
1979 pub struct ProgramForMerkleTreeUpdate {
1980 leaved_merkle_tree: LeavedMerkleTreeTestData,
1981
1982 #[strategy(0..#leaved_merkle_tree.revealed_indices.len())]
1983 #[map(|i| #leaved_merkle_tree.revealed_indices[i])]
1984 revealed_leafs_index: usize,
1985
1986 #[strategy(arb())]
1987 new_leaf: Digest,
1988
1989 #[strategy(arb())]
1990 auth_path_address: BFieldElement,
1991 }
1992
1993 impl ProgramForMerkleTreeUpdate {
1994 pub fn assemble(self) -> TestableProgram {
1995 let auth_path = self.authentication_path();
1996 let leaf_index = self.revealed_leafs_index;
1997 let merkle_tree = self.leaved_merkle_tree.merkle_tree;
1998
1999 let ram = (self.auth_path_address.value()..)
2000 .map(BFieldElement::new)
2001 .zip(auth_path.iter().flat_map(|digest| digest.values()))
2002 .collect::<HashMap<_, _>>();
2003 let non_determinism = NonDeterminism::default().with_ram(ram);
2004
2005 let old_leaf = Digest::from(self.leaved_merkle_tree.leaves[leaf_index]);
2006 let old_root = merkle_tree.root();
2007 let mut public_input =
2008 bfe_vec![self.auth_path_address, leaf_index, merkle_tree.height()];
2009 public_input.extend(old_leaf.reversed().values());
2010 public_input.extend(old_root.reversed().values());
2011 public_input.extend(self.new_leaf.reversed().values());
2012
2013 TestableProgram::new(crate::example_programs::MERKLE_TREE_UPDATE.clone())
2014 .with_input(public_input)
2015 .with_non_determinism(non_determinism)
2016 }
2017
2018 #[must_use]
2023 pub fn is_integral(&self) -> bool {
2024 let inclusion_proof = MerkleTreeInclusionProof {
2025 tree_height: self.leaved_merkle_tree.merkle_tree.height(),
2026 indexed_leafs: vec![(self.revealed_leafs_index, self.new_leaf)],
2027 authentication_structure: self.authentication_path(),
2028 };
2029
2030 let new_root = self.clone().assemble().run().unwrap();
2031 let new_root = Digest(new_root.try_into().unwrap());
2032 inclusion_proof.verify(new_root)
2033 }
2034
2035 fn authentication_path(&self) -> Vec<Digest> {
2039 self.leaved_merkle_tree
2040 .merkle_tree
2041 .authentication_structure(&[self.revealed_leafs_index])
2042 .unwrap()
2043 }
2044 }
2045
2046 pub(crate) fn test_program_for_assert_vector() -> TestableProgram {
2047 TestableProgram::new(triton_program!(
2048 push 1 push 2 push 3 push 4 push 5
2049 push 1 push 2 push 3 push 4 push 5
2050 assert_vector halt
2051 ))
2052 }
2053
2054 pub(crate) fn test_program_for_sponge_instructions() -> TestableProgram {
2055 let push_10_zeros = triton_asm![push 0; 10];
2056 TestableProgram::new(triton_program!(
2057 sponge_init
2058 {&push_10_zeros} sponge_absorb
2059 {&push_10_zeros} sponge_absorb
2060 sponge_squeeze halt
2061 ))
2062 }
2063
2064 pub(crate) fn test_program_for_sponge_instructions_2() -> TestableProgram {
2065 let push_5_zeros = triton_asm![push 0; 5];
2066 let program = triton_program! {
2067 sponge_init
2068 sponge_squeeze sponge_absorb
2069 {&push_5_zeros} hash
2070 sponge_squeeze sponge_absorb
2071 halt
2072 };
2073 TestableProgram::new(program)
2074 }
2075
2076 pub(crate) fn test_program_for_many_sponge_instructions() -> TestableProgram {
2077 let push_5_zeros = triton_asm![push 0; 5];
2078 let push_10_zeros = triton_asm![push 0; 10];
2079 let program = triton_program! { sponge_init sponge_squeeze sponge_absorb {&push_10_zeros} sponge_absorb {&push_10_zeros} sponge_absorb sponge_squeeze sponge_squeeze sponge_squeeze sponge_absorb sponge_init sponge_init sponge_init sponge_absorb sponge_init sponge_squeeze sponge_squeeze sponge_init sponge_squeeze {&push_5_zeros} hash sponge_absorb {&push_5_zeros} hash sponge_squeeze {&push_5_zeros} hash sponge_absorb {&push_5_zeros} hash sponge_squeeze sponge_init sponge_absorb sponge_absorb sponge_absorb sponge_absorb {&push_10_zeros} sponge_absorb {&push_10_zeros} sponge_absorb {&push_10_zeros} sponge_absorb halt
2102 };
2103 TestableProgram::new(program)
2104 }
2105
2106 pub(crate) fn property_based_test_program_for_assert_vector() -> TestableProgram {
2107 let mut rng = ThreadRng::default();
2108 let st: [BFieldElement; 5] = rng.random();
2109
2110 let program = triton_program!(
2111 push {st[0]} push {st[1]} push {st[2]} push {st[3]} push {st[4]}
2112 read_io 5 assert_vector halt
2113 );
2114
2115 TestableProgram::new(program).with_input(st)
2116 }
2117
2118 #[derive(Debug, Copy, Clone, Eq, PartialEq, EnumCount, EnumIter, test_strategy::Arbitrary)]
2120 enum SpongeAndHashInstructions {
2121 SpongeInit,
2122 SpongeAbsorb(#[strategy(arb())] [BFieldElement; tip5::RATE]),
2123 SpongeAbsorbMem(#[strategy(arb())] BFieldElement),
2124 SpongeSqueeze,
2125 Hash(#[strategy(arb())] [BFieldElement; tip5::RATE]),
2126 }
2127
2128 impl SpongeAndHashInstructions {
2129 fn to_vm_instruction_sequence(self) -> Vec<Instruction> {
2130 let push_array =
2131 |a: [_; tip5::RATE]| a.into_iter().rev().map(Instruction::Push).collect_vec();
2132
2133 match self {
2134 Self::SpongeInit => vec![Instruction::SpongeInit],
2135 Self::SpongeAbsorb(input) => {
2136 [push_array(input), vec![Instruction::SpongeAbsorb]].concat()
2137 }
2138 Self::SpongeAbsorbMem(ram_pointer) => {
2139 vec![Instruction::Push(ram_pointer), Instruction::SpongeAbsorbMem]
2140 }
2141 Self::SpongeSqueeze => vec![Instruction::SpongeSqueeze],
2142 Self::Hash(input) => [push_array(input), vec![Instruction::Hash]].concat(),
2143 }
2144 }
2145
2146 fn execute(self, sponge: &mut Tip5, ram: &HashMap<BFieldElement, BFieldElement>) {
2147 let ram_read = |p| ram.get(&p).copied().unwrap_or_else(|| bfe!(0));
2148 let ram_read_array = |p| {
2149 let ram_reads = (0..tip5::RATE as u32).map(|i| ram_read(p + bfe!(i)));
2150 ram_reads.collect_vec().try_into().unwrap()
2151 };
2152
2153 match self {
2154 Self::SpongeInit => *sponge = Tip5::init(),
2155 Self::SpongeAbsorb(input) => sponge.absorb(input),
2156 Self::SpongeAbsorbMem(ram_pointer) => sponge.absorb(ram_read_array(ram_pointer)),
2157 Self::SpongeSqueeze => _ = sponge.squeeze(),
2158 Self::Hash(_) => (), }
2160 }
2161 }
2162
2163 #[derive(Debug, Clone, Eq, PartialEq, test_strategy::Arbitrary)]
2165 pub struct ProgramForSpongeAndHashInstructions {
2166 instructions: Vec<SpongeAndHashInstructions>,
2167
2168 #[strategy(arb())]
2169 ram: HashMap<BFieldElement, BFieldElement>,
2170 }
2171
2172 impl ProgramForSpongeAndHashInstructions {
2173 pub fn assemble(self) -> TestableProgram {
2174 let mut sponge = Tip5::init();
2175 for instruction in &self.instructions {
2176 instruction.execute(&mut sponge, &self.ram);
2177 }
2178 let expected_rate = sponge.squeeze();
2179 let non_determinism = NonDeterminism::default().with_ram(self.ram);
2180
2181 let sponge_and_hash_instructions = self
2182 .instructions
2183 .into_iter()
2184 .flat_map(|i| i.to_vm_instruction_sequence())
2185 .collect_vec();
2186 let output_equality_assertions =
2187 vec![triton_asm![read_io 1 eq assert]; tip5::RATE].concat();
2188
2189 let program = triton_program! {
2190 sponge_init
2191 {&sponge_and_hash_instructions}
2192 sponge_squeeze
2193 {&output_equality_assertions}
2194 halt
2195 };
2196
2197 TestableProgram::new(program)
2198 .with_input(expected_rate)
2199 .with_non_determinism(non_determinism)
2200 }
2201 }
2202
2203 #[proptest]
2204 fn property_based_sponge_and_hash_instructions_program_sanity_check(
2205 program: ProgramForSpongeAndHashInstructions,
2206 ) {
2207 program.assemble().run()?;
2208 }
2209
2210 pub(crate) fn test_program_for_add_mul_invert() -> TestableProgram {
2211 TestableProgram::new(triton_program!(
2212 push 2 push -1 add assert
2213 push -1 push -1 mul assert
2214 push 5 addi -4 assert
2215 push 3 dup 0 invert mul assert
2216 halt
2217 ))
2218 }
2219
2220 pub(crate) fn property_based_test_program_for_split() -> TestableProgram {
2221 let mut rng = ThreadRng::default();
2222 let st0 = rng.next_u64() % BFieldElement::P;
2223 let hi = st0 >> 32;
2224 let lo = st0 & 0xffff_ffff;
2225
2226 let program =
2227 triton_program!(push {st0} split read_io 1 eq assert read_io 1 eq assert halt);
2228 TestableProgram::new(program).with_input(bfe_array![lo, hi])
2229 }
2230
2231 pub(crate) fn test_program_for_eq() -> TestableProgram {
2232 let input = bfe_array![42];
2233 TestableProgram::new(triton_program!(read_io 1 divine 1 eq assert halt))
2234 .with_input(input)
2235 .with_non_determinism(input)
2236 }
2237
2238 pub(crate) fn property_based_test_program_for_eq() -> TestableProgram {
2239 let mut rng = ThreadRng::default();
2240 let st0 = rng.next_u64() % BFieldElement::P;
2241 let input = bfe_array![st0];
2242
2243 let program =
2244 triton_program!(push {st0} dup 0 read_io 1 eq assert dup 0 divine 1 eq assert halt);
2245 TestableProgram::new(program)
2246 .with_input(input)
2247 .with_non_determinism(input)
2248 }
2249
2250 pub(crate) fn test_program_for_lsb() -> TestableProgram {
2251 TestableProgram::new(triton_program!(
2252 push 3 call lsb assert assert halt
2253 lsb:
2254 push 2 swap 1 div_mod return
2255 ))
2256 }
2257
2258 pub(crate) fn property_based_test_program_for_lsb() -> TestableProgram {
2259 let mut rng = ThreadRng::default();
2260 let st0 = rng.next_u32();
2261 let lsb = st0 % 2;
2262 let st0_shift_right = st0 >> 1;
2263
2264 let program = triton_program!(
2265 push {st0} call lsb read_io 1 eq assert read_io 1 eq assert halt
2266 lsb:
2267 push 2 swap 1 div_mod return
2268 );
2269 TestableProgram::new(program).with_input([lsb, st0_shift_right].map(|b| bfe!(b)))
2270 }
2271
2272 pub(crate) fn test_program_0_lt_0() -> TestableProgram {
2273 TestableProgram::new(triton_program!(push 0 push 0 lt halt))
2274 }
2275
2276 pub(crate) fn test_program_for_lt() -> TestableProgram {
2277 TestableProgram::new(triton_program!(
2278 push 5 push 2 lt assert push 2 push 5 lt push 0 eq assert halt
2279 ))
2280 }
2281
2282 pub(crate) fn property_based_test_program_for_lt() -> TestableProgram {
2283 let mut rng = ThreadRng::default();
2284
2285 let st1_0 = rng.next_u32();
2286 let st0_0 = rng.next_u32();
2287 let result_0 = match st0_0 < st1_0 {
2288 true => 1,
2289 false => 0,
2290 };
2291
2292 let st1_1 = rng.next_u32();
2293 let st0_1 = rng.next_u32();
2294 let result_1 = match st0_1 < st1_1 {
2295 true => 1,
2296 false => 0,
2297 };
2298
2299 let program = triton_program!(
2300 push {st1_0} push {st0_0} lt read_io 1 eq assert
2301 push {st1_1} push {st0_1} lt read_io 1 eq assert halt
2302 );
2303 TestableProgram::new(program).with_input([result_0, result_1].map(|b| bfe!(b)))
2304 }
2305
2306 pub(crate) fn test_program_for_and() -> TestableProgram {
2307 TestableProgram::new(triton_program!(
2308 push 5 push 3 and assert push 12 push 5 and push 4 eq assert halt
2309 ))
2310 }
2311
2312 pub(crate) fn property_based_test_program_for_and() -> TestableProgram {
2313 let mut rng = ThreadRng::default();
2314
2315 let st1_0 = rng.next_u32();
2316 let st0_0 = rng.next_u32();
2317 let result_0 = st0_0.bitand(st1_0);
2318
2319 let st1_1 = rng.next_u32();
2320 let st0_1 = rng.next_u32();
2321 let result_1 = st0_1.bitand(st1_1);
2322
2323 let program = triton_program!(
2324 push {st1_0} push {st0_0} and read_io 1 eq assert
2325 push {st1_1} push {st0_1} and read_io 1 eq assert halt
2326 );
2327 TestableProgram::new(program).with_input([result_0, result_1].map(|b| bfe!(b)))
2328 }
2329
2330 pub(crate) fn test_program_for_xor() -> TestableProgram {
2331 TestableProgram::new(triton_program!(
2332 push 7 push 6 xor assert push 5 push 12 xor push 9 eq assert halt
2333 ))
2334 }
2335
2336 pub(crate) fn property_based_test_program_for_xor() -> TestableProgram {
2337 let mut rng = ThreadRng::default();
2338
2339 let st1_0 = rng.next_u32();
2340 let st0_0 = rng.next_u32();
2341 let result_0 = st0_0.bitxor(st1_0);
2342
2343 let st1_1 = rng.next_u32();
2344 let st0_1 = rng.next_u32();
2345 let result_1 = st0_1.bitxor(st1_1);
2346
2347 let program = triton_program!(
2348 push {st1_0} push {st0_0} xor read_io 1 eq assert
2349 push {st1_1} push {st0_1} xor read_io 1 eq assert halt
2350 );
2351 TestableProgram::new(program).with_input([result_0, result_1].map(|b| bfe!(b)))
2352 }
2353
2354 pub(crate) fn test_program_for_log2floor() -> TestableProgram {
2355 TestableProgram::new(triton_program!(
2356 push 1 log_2_floor push 0 eq assert
2357 push 2 log_2_floor push 1 eq assert
2358 push 3 log_2_floor push 1 eq assert
2359 push 4 log_2_floor push 2 eq assert
2360 push 7 log_2_floor push 2 eq assert
2361 push 8 log_2_floor push 3 eq assert
2362 push 15 log_2_floor push 3 eq assert
2363 push 16 log_2_floor push 4 eq assert
2364 push 31 log_2_floor push 4 eq assert
2365 push 32 log_2_floor push 5 eq assert
2366 push 33 log_2_floor push 5 eq assert
2367 push 4294967295 log_2_floor push 31 eq assert halt
2368 ))
2369 }
2370
2371 pub(crate) fn property_based_test_program_for_log2floor() -> TestableProgram {
2372 let mut rng = ThreadRng::default();
2373
2374 let st0_0 = rng.next_u32();
2375 let l2f_0 = st0_0.ilog2();
2376
2377 let st0_1 = rng.next_u32();
2378 let l2f_1 = st0_1.ilog2();
2379
2380 let program = triton_program!(
2381 push {st0_0} log_2_floor read_io 1 eq assert
2382 push {st0_1} log_2_floor read_io 1 eq assert halt
2383 );
2384 TestableProgram::new(program).with_input([l2f_0, l2f_1].map(|b| bfe!(b)))
2385 }
2386
2387 pub(crate) fn test_program_for_pow() -> TestableProgram {
2388 TestableProgram::new(triton_program!(
2389 push 0 push 0 pow push 1 eq assert
2391 push 0 push 1 pow push 1 eq assert
2392 push 0 push 2 pow push 1 eq assert
2393 push 1 push 0 pow push 0 eq assert
2394 push 2 push 0 pow push 0 eq assert
2395 push 2 push 5 pow push 25 eq assert
2396 push 5 push 2 pow push 32 eq assert
2397 push 10 push 2 pow push 1024 eq assert
2398 push 3 push 3 pow push 27 eq assert
2399 push 3 push 5 pow push 125 eq assert
2400 push 9 push 7 pow push 40353607 eq assert
2401 push 3040597274 push 05218640216028681988 pow push 11160453713534536216 eq assert
2402 push 2378067562 push 13711477740065654150 pow push 06848017529532358230 eq assert
2403 push 129856251 push 00218966585049330803 pow push 08283208434666229347 eq assert
2404 push 1657936293 push 04999758396092641065 pow push 11426020017566937356 eq assert
2405 push 3474149688 push 05702231339458623568 pow push 02862889945380025510 eq assert
2406 push 2243935791 push 09059335263701504667 pow push 04293137302922963369 eq assert
2407 push 1783029319 push 00037306102533222534 pow push 10002149917806048099 eq assert
2408 push 3608140376 push 17716542154416103060 pow push 11885601801443303960 eq assert
2409 push 1220084884 push 07207865095616988291 pow push 05544378138345942897 eq assert
2410 push 3539668245 push 13491612301110950186 pow push 02612675697712040250 eq assert
2411 halt
2412 ))
2413 }
2414
2415 pub(crate) fn property_based_test_program_for_pow() -> TestableProgram {
2416 let mut rng = ThreadRng::default();
2417
2418 let base_0: BFieldElement = rng.random();
2419 let exp_0 = rng.next_u32();
2420 let result_0 = base_0.mod_pow_u32(exp_0);
2421
2422 let base_1: BFieldElement = rng.random();
2423 let exp_1 = rng.next_u32();
2424 let result_1 = base_1.mod_pow_u32(exp_1);
2425
2426 let program = triton_program!(
2427 push {exp_0} push {base_0} pow read_io 1 eq assert
2428 push {exp_1} push {base_1} pow read_io 1 eq assert halt
2429 );
2430 TestableProgram::new(program).with_input([result_0, result_1])
2431 }
2432
2433 pub(crate) fn test_program_for_div_mod() -> TestableProgram {
2434 TestableProgram::new(triton_program!(push 2 push 3 div_mod assert assert halt))
2435 }
2436
2437 pub(crate) fn property_based_test_program_for_div_mod() -> TestableProgram {
2438 let mut rng = ThreadRng::default();
2439
2440 let denominator = rng.next_u32();
2441 let numerator = rng.next_u32();
2442 let quotient = numerator / denominator;
2443 let remainder = numerator % denominator;
2444
2445 let program = triton_program!(
2446 push {denominator} push {numerator} div_mod read_io 1 eq assert read_io 1 eq assert halt
2447 );
2448 TestableProgram::new(program).with_input([remainder, quotient].map(|b| bfe!(b)))
2449 }
2450
2451 pub(crate) fn test_program_for_starting_with_pop_count() -> TestableProgram {
2452 TestableProgram::new(triton_program!(pop_count dup 0 push 0 eq assert halt))
2453 }
2454
2455 pub(crate) fn test_program_for_pop_count() -> TestableProgram {
2456 TestableProgram::new(triton_program!(push 10 pop_count push 2 eq assert halt))
2457 }
2458
2459 pub(crate) fn property_based_test_program_for_pop_count() -> TestableProgram {
2460 let mut rng = ThreadRng::default();
2461 let st0 = rng.next_u32();
2462 let pop_count = st0.count_ones();
2463 let program = triton_program!(push {st0} pop_count read_io 1 eq assert halt);
2464 TestableProgram::new(program).with_input(bfe_array![pop_count])
2465 }
2466
2467 pub(crate) fn property_based_test_program_for_is_u32() -> TestableProgram {
2468 let mut rng = ThreadRng::default();
2469 let st0_u32 = rng.next_u32();
2470 let st0_not_u32 = (u64::from(rng.next_u32()) << 32) + u64::from(rng.next_u32());
2471 let program = triton_program!(
2472 push {st0_u32} call is_u32 assert
2473 push {st0_not_u32} call is_u32 push 0 eq assert halt
2474 is_u32:
2475 split pop 1 push 0 eq return
2476 );
2477 TestableProgram::new(program)
2478 }
2479
2480 pub(crate) fn property_based_test_program_for_random_ram_access() -> TestableProgram {
2481 let mut rng = ThreadRng::default();
2482 let num_memory_accesses = rng.random_range(10..50);
2483 let memory_addresses: Vec<BFieldElement> = random_elements(num_memory_accesses);
2484 let mut memory_values: Vec<BFieldElement> = random_elements(num_memory_accesses);
2485 let mut instructions = vec![];
2486
2487 for address in memory_addresses.iter().take(num_memory_accesses / 4) {
2493 instructions.extend(triton_asm!(push {address} read_mem 1 pop 1 push 0 eq assert));
2494 }
2495
2496 for (address, value) in memory_addresses.iter().zip_eq(memory_values.iter()) {
2498 instructions.extend(triton_asm!(push {value} push {address} write_mem 1 pop 1));
2499 }
2500
2501 let mut reading_permutation = (0..num_memory_accesses).collect_vec();
2503 for i in 0..num_memory_accesses {
2504 let j = rng.random_range(0..num_memory_accesses);
2505 reading_permutation.swap(i, j);
2506 }
2507 for idx in reading_permutation {
2508 let address = memory_addresses[idx];
2509 let value = memory_values[idx];
2510 instructions
2511 .extend(triton_asm!(push {address} read_mem 1 pop 1 push {value} eq assert));
2512 }
2513
2514 let mut writing_permutation = (0..num_memory_accesses).collect_vec();
2516 for i in 0..num_memory_accesses {
2517 let j = rng.random_range(0..num_memory_accesses);
2518 writing_permutation.swap(i, j);
2519 }
2520 for idx in 0..num_memory_accesses / 2 {
2521 let address = memory_addresses[writing_permutation[idx]];
2522 let new_memory_value = rng.random();
2523 memory_values[writing_permutation[idx]] = new_memory_value;
2524 instructions
2525 .extend(triton_asm!(push {new_memory_value} push {address} write_mem 1 pop 1));
2526 }
2527
2528 let mut reading_permutation = (0..num_memory_accesses).collect_vec();
2531 for i in 0..num_memory_accesses {
2532 let j = rng.random_range(0..num_memory_accesses);
2533 reading_permutation.swap(i, j);
2534 }
2535 for idx in reading_permutation {
2536 let address = memory_addresses[idx];
2537 let value = memory_values[idx];
2538 instructions
2539 .extend(triton_asm!(push {address} read_mem 1 pop 1 push {value} eq assert));
2540 }
2541
2542 let program = triton_program! { { &instructions } halt };
2543 TestableProgram::new(program)
2544 }
2545
2546 #[test]
2549 fn run_dont_prove_property_based_test_for_random_ram_access() {
2550 let program = property_based_test_program_for_random_ram_access();
2551 program.run().unwrap();
2552 }
2553
2554 #[test]
2555 fn can_compute_dot_product_from_uninitialized_ram() {
2556 let program = triton_program!(xx_dot_step xb_dot_step halt);
2557 VM::run(program, PublicInput::default(), NonDeterminism::default()).unwrap();
2558 }
2559
2560 pub(crate) fn property_based_test_program_for_xx_dot_step() -> TestableProgram {
2561 let mut rng = ThreadRng::default();
2562 let n = rng.random_range(0..10);
2563
2564 let push_xfe = |xfe: XFieldElement| {
2565 let [c_0, c_1, c_2] = xfe.coefficients;
2566 triton_asm! { push {c_2} push {c_1} push {c_0} }
2567 };
2568 let push_and_write_xfe = |xfe| {
2569 let push_xfe = push_xfe(xfe);
2570 triton_asm! {
2571 {&push_xfe}
2572 dup 3
2573 write_mem 3
2574 swap 1
2575 pop 1
2576 }
2577 };
2578 let into_write_instructions = |elements: Vec<_>| {
2579 elements
2580 .into_iter()
2581 .flat_map(push_and_write_xfe)
2582 .collect_vec()
2583 };
2584
2585 let vector_one = (0..n).map(|_| rng.random()).collect_vec();
2586 let vector_two = (0..n).map(|_| rng.random()).collect_vec();
2587 let inner_product = vector_one
2588 .iter()
2589 .zip(&vector_two)
2590 .map(|(&a, &b)| a * b)
2591 .sum();
2592 let push_inner_product = push_xfe(inner_product);
2593
2594 let push_and_write_vector_one = into_write_instructions(vector_one);
2595 let push_and_write_vector_two = into_write_instructions(vector_two);
2596 let many_dotsteps = (0..n).map(|_| triton_instr!(xx_dot_step)).collect_vec();
2597
2598 let code = triton_program! {
2599 push 0
2600 {&push_and_write_vector_one}
2601 dup 0
2602 {&push_and_write_vector_two}
2603 pop 1
2604 push 0
2605
2606 {&many_dotsteps}
2607 pop 2
2608 push 0 push 0
2609
2610 {&push_inner_product}
2611 push 0 push 0
2612 assert_vector
2613 halt
2614 };
2615 TestableProgram::new(code)
2616 }
2617
2618 #[test]
2620 fn run_dont_prove_property_based_test_program_for_xx_dot_step() {
2621 let program = property_based_test_program_for_xx_dot_step();
2622 program.run().unwrap();
2623 }
2624
2625 pub(crate) fn property_based_test_program_for_xb_dot_step() -> TestableProgram {
2626 let mut rng = ThreadRng::default();
2627 let n = rng.random_range(0..10);
2628 let push_xfe = |x: XFieldElement| {
2629 triton_asm! {
2630 push {x.coefficients[2]}
2631 push {x.coefficients[1]}
2632 push {x.coefficients[0]}
2633 }
2634 };
2635 let push_and_write_xfe = |x: XFieldElement| {
2636 triton_asm! {
2637 push {x.coefficients[2]}
2638 push {x.coefficients[1]}
2639 push {x.coefficients[0]}
2640 dup 3
2641 write_mem 3
2642 swap 1
2643 pop 1
2644 }
2645 };
2646 let push_and_write_bfe = |x: BFieldElement| {
2647 triton_asm! {
2648 push {x}
2649 dup 1
2650 write_mem 1
2651 swap 1
2652 pop 1
2653 }
2654 };
2655 let vector_one = (0..n).map(|_| rng.random::<XFieldElement>()).collect_vec();
2656 let vector_two = (0..n).map(|_| rng.random::<BFieldElement>()).collect_vec();
2657 let inner_product = vector_one
2658 .iter()
2659 .zip(vector_two.iter())
2660 .map(|(&a, &b)| a * b)
2661 .sum::<XFieldElement>();
2662 let push_and_write_vector_one = (0..n)
2663 .flat_map(|i| push_and_write_xfe(vector_one[i]))
2664 .collect_vec();
2665 let push_and_write_vector_two = (0..n)
2666 .flat_map(|i| push_and_write_bfe(vector_two[i]))
2667 .collect_vec();
2668 let push_inner_product = push_xfe(inner_product);
2669 let many_dotsteps = (0..n).map(|_| triton_instr!(xb_dot_step)).collect_vec();
2670 let code = triton_program! {
2671 push 0
2672 {&push_and_write_vector_one}
2673 dup 0
2674 {&push_and_write_vector_two}
2675 pop 1
2676 push 0
2677 swap 1
2678 {&many_dotsteps}
2679 pop 1
2680 pop 1
2681 push 0
2682 push 0
2683 {&push_inner_product}
2684 push 0
2685 push 0
2686 assert_vector
2687 halt
2688 };
2689 TestableProgram::new(code)
2690 }
2691
2692 #[test]
2694 fn run_dont_prove_property_based_test_program_for_xb_dot_step() {
2695 let program = property_based_test_program_for_xb_dot_step();
2696 program.run().unwrap();
2697 }
2698
2699 #[proptest]
2700 fn negative_property_is_u32(
2701 #[strategy(arb())]
2702 #[filter(#st0.value() > u64::from(u32::MAX))]
2703 st0: BFieldElement,
2704 ) {
2705 let program = triton_program!(
2706 push {st0} call is_u32 assert halt
2707 is_u32:
2708 split pop 1 push 0 eq return
2709 );
2710 let program = TestableProgram::new(program);
2711 let_assert!(Err(err) = program.run());
2712 let_assert!(InstructionError::AssertionFailed(_) = err.source);
2713 }
2714
2715 pub(crate) fn test_program_for_split() -> TestableProgram {
2716 TestableProgram::new(triton_program!(
2717 push -2 split push 4294967295 eq assert push 4294967294 eq assert
2718 push -1 split push 0 eq assert push 4294967295 eq assert
2719 push 0 split push 0 eq assert push 0 eq assert
2720 push 1 split push 1 eq assert push 0 eq assert
2721 push 2 split push 2 eq assert push 0 eq assert
2722 push 4294967297 split assert assert
2723 halt
2724 ))
2725 }
2726
2727 pub(crate) fn test_program_for_xx_add() -> TestableProgram {
2728 TestableProgram::new(triton_program!(
2729 push 5 push 6 push 7 push 8 push 9 push 10 xx_add halt
2730 ))
2731 }
2732
2733 pub(crate) fn test_program_for_xx_mul() -> TestableProgram {
2734 TestableProgram::new(triton_program!(
2735 push 5 push 6 push 7 push 8 push 9 push 10 xx_mul halt
2736 ))
2737 }
2738
2739 pub(crate) fn test_program_for_x_invert() -> TestableProgram {
2740 TestableProgram::new(triton_program!(
2741 push 5 push 6 push 7 x_invert halt
2742 ))
2743 }
2744
2745 pub(crate) fn test_program_for_xb_mul() -> TestableProgram {
2746 TestableProgram::new(triton_program!(
2747 push 5 push 6 push 7 push 8 xb_mul halt
2748 ))
2749 }
2750
2751 pub(crate) fn test_program_for_read_io_write_io() -> TestableProgram {
2752 let program = triton_program!(
2753 read_io 1 assert read_io 2 dup 1 dup 1 add write_io 1 mul push 5 write_io 2 halt
2754 );
2755 TestableProgram::new(program).with_input([1, 3, 14].map(|b| bfe!(b)))
2756 }
2757
2758 pub(crate) fn test_program_claim_in_ram_corresponds_to_currently_running_program()
2759 -> TestableProgram {
2760 let program = triton_program! {
2761 dup 15 dup 15 dup 15 dup 15 dup 15 push 4 read_mem 5 pop 1 assert_vector halt
2765 };
2766
2767 let program_digest = program.hash();
2768 let enumerated_digest_elements = program_digest.values().into_iter().enumerate();
2769 let initial_ram = enumerated_digest_elements
2770 .map(|(address, d)| (bfe!(u64::try_from(address).unwrap()), d))
2771 .collect::<HashMap<_, _>>();
2772 let non_determinism = NonDeterminism::default().with_ram(initial_ram);
2773
2774 TestableProgram::new(program).with_non_determinism(non_determinism)
2775 }
2776
2777 #[proptest]
2778 fn xx_add(
2779 #[strategy(arb())] left_operand: XFieldElement,
2780 #[strategy(arb())] right_operand: XFieldElement,
2781 ) {
2782 let program = triton_program!(
2783 push {right_operand.coefficients[2]}
2784 push {right_operand.coefficients[1]}
2785 push {right_operand.coefficients[0]}
2786 push {left_operand.coefficients[2]}
2787 push {left_operand.coefficients[1]}
2788 push {left_operand.coefficients[0]}
2789 xx_add
2790 write_io 3
2791 halt
2792 );
2793 let actual_stdout = VM::run(program, [].into(), [].into())?;
2794 let expected_stdout = (left_operand + right_operand).coefficients.to_vec();
2795 prop_assert_eq!(expected_stdout, actual_stdout);
2796 }
2797
2798 #[proptest]
2799 fn xx_mul(
2800 #[strategy(arb())] left_operand: XFieldElement,
2801 #[strategy(arb())] right_operand: XFieldElement,
2802 ) {
2803 let program = triton_program!(
2804 push {right_operand.coefficients[2]}
2805 push {right_operand.coefficients[1]}
2806 push {right_operand.coefficients[0]}
2807 push {left_operand.coefficients[2]}
2808 push {left_operand.coefficients[1]}
2809 push {left_operand.coefficients[0]}
2810 xx_mul
2811 write_io 3
2812 halt
2813 );
2814 let actual_stdout = VM::run(program, [].into(), [].into())?;
2815 let expected_stdout = (left_operand * right_operand).coefficients.to_vec();
2816 prop_assert_eq!(expected_stdout, actual_stdout);
2817 }
2818
2819 #[proptest]
2820 fn xinv(
2821 #[strategy(arb())]
2822 #[filter(!#operand.is_zero())]
2823 operand: XFieldElement,
2824 ) {
2825 let program = triton_program!(
2826 push {operand.coefficients[2]}
2827 push {operand.coefficients[1]}
2828 push {operand.coefficients[0]}
2829 x_invert
2830 write_io 3
2831 halt
2832 );
2833 let actual_stdout = VM::run(program, [].into(), [].into())?;
2834 let expected_stdout = operand.inverse().coefficients.to_vec();
2835 prop_assert_eq!(expected_stdout, actual_stdout);
2836 }
2837
2838 #[proptest]
2839 fn xb_mul(#[strategy(arb())] scalar: BFieldElement, #[strategy(arb())] operand: XFieldElement) {
2840 let program = triton_program!(
2841 push {operand.coefficients[2]}
2842 push {operand.coefficients[1]}
2843 push {operand.coefficients[0]}
2844 push {scalar}
2845 xb_mul
2846 write_io 3
2847 halt
2848 );
2849 let actual_stdout = VM::run(program, [].into(), [].into())?;
2850 let expected_stdout = (scalar * operand).coefficients.to_vec();
2851 prop_assert_eq!(expected_stdout, actual_stdout);
2852 }
2853
2854 #[proptest]
2855 fn pseudo_sub(
2856 #[strategy(arb())] minuend: BFieldElement,
2857 #[strategy(arb())] subtrahend: BFieldElement,
2858 ) {
2859 let program = triton_program!(
2860 push {subtrahend} push {minuend} call sub write_io 1 halt
2861 sub:
2862 swap 1 push -1 mul add return
2863 );
2864 let actual_stdout = TestableProgram::new(program).run()?;
2865 let expected_stdout = vec![minuend - subtrahend];
2866
2867 prop_assert_eq!(expected_stdout, actual_stdout);
2868 }
2869
2870 const _OP_STACK_IS_BIG_ENOUGH: () = std::assert!(2 * Digest::LEN <= OpStackElement::COUNT);
2872
2873 #[test]
2874 fn run_tvm_hello_world() {
2875 let program = triton_program!(
2876 push 10 push 33 push 100 push 108 push 114 push 111 push 87 push 32 push 44 push 111 push 108 push 108 push 101 push 72 write_io 5 write_io 5 write_io 4
2891 halt
2892 );
2893 let mut vm_state = VMState::new(program, [].into(), [].into());
2894 let_assert!(Ok(()) = vm_state.run());
2895 assert!(BFieldElement::ZERO == vm_state.op_stack[0]);
2896 }
2897
2898 #[test]
2899 fn run_tvm_merkle_step_right_sibling() {
2900 let program = test_program_for_merkle_step_right_sibling();
2901 let_assert!(Ok(_) = program.run());
2902 }
2903
2904 #[test]
2905 fn run_tvm_merkle_step_left_sibling() {
2906 let program = test_program_for_merkle_step_left_sibling();
2907 let_assert!(Ok(_) = program.run());
2908 }
2909
2910 #[test]
2911 fn run_tvm_merkle_step_mem_right_sibling() {
2912 let program = test_program_for_merkle_step_mem_right_sibling();
2913 let_assert!(Ok(_) = program.run());
2914 }
2915
2916 #[test]
2917 fn run_tvm_merkle_step_mem_left_sibling() {
2918 let program = test_program_for_merkle_step_mem_left_sibling();
2919 let_assert!(Ok(_) = program.run());
2920 }
2921
2922 #[test]
2923 fn run_tvm_halt_then_do_stuff() {
2924 let program = triton_program!(halt push 1 push 2 add invert write_io 5);
2925 let_assert!(Ok((aet, _)) = VM::trace_execution(program, [].into(), [].into()));
2926
2927 let_assert!(Some(last_processor_row) = aet.processor_trace.rows().into_iter().next_back());
2928 let clk_count = last_processor_row[ProcessorMainColumn::CLK.main_index()];
2929 assert!(BFieldElement::ZERO == clk_count);
2930
2931 let last_instruction = last_processor_row[ProcessorMainColumn::CI.main_index()];
2932 assert!(Instruction::Halt.opcode_b() == last_instruction);
2933
2934 println!("{last_processor_row}");
2935 }
2936
2937 #[test]
2938 fn run_tvm_basic_ram_read_write() {
2939 let program = triton_program!(
2940 push 8 push 5 write_mem 1 pop 1 push 18 push 15 write_mem 1 pop 1 push 5 read_mem 1 pop 2 push 15 read_mem 1 pop 2 push 7 push 5 write_mem 1 pop 1 push 15 read_mem 1 push 5 read_mem 1 halt
2948 );
2949
2950 let mut vm_state = VMState::new(program, [].into(), [].into());
2951 let_assert!(Ok(()) = vm_state.run());
2952 assert!(4 == vm_state.op_stack[0].value());
2953 assert!(7 == vm_state.op_stack[1].value());
2954 assert!(14 == vm_state.op_stack[2].value());
2955 assert!(18 == vm_state.op_stack[3].value());
2956 }
2957
2958 #[test]
2959 fn run_tvm_edgy_ram_writes() {
2960 let program = triton_program!(
2961 push 0 write_mem 1 push 5 swap 1 push 3 swap 1 pop 1 write_mem 1 push -1 add read_mem 1 swap 2 pop 1 swap 1 push 1 add read_mem 1 halt
2981 );
2982
2983 let mut vm_state = VMState::new(program, [].into(), [].into());
2984 let_assert!(Ok(()) = vm_state.run());
2985 assert!(2_u64 == vm_state.op_stack[0].value());
2986 assert!(5_u64 == vm_state.op_stack[1].value());
2987 assert!(5_u64 == vm_state.op_stack[2].value());
2988 }
2989
2990 #[proptest]
2991 fn triton_assembly_merkle_tree_authentication_path_verification(
2992 leaved_merkle_tree: LeavedMerkleTreeTestData,
2993 ) {
2994 let merkle_tree = &leaved_merkle_tree.merkle_tree;
2995 let auth_path = |&i| merkle_tree.authentication_structure(&[i]).unwrap();
2996
2997 let revealed_indices = &leaved_merkle_tree.revealed_indices;
2998 let num_authentication_paths = revealed_indices.len();
2999
3000 let auth_paths = revealed_indices.iter().flat_map(auth_path).collect_vec();
3001 let non_determinism = NonDeterminism::default().with_digests(auth_paths);
3002
3003 let mut public_input = vec![(num_authentication_paths as u64).into()];
3004 public_input.extend(leaved_merkle_tree.root().reversed().values());
3005 for &leaf_index in revealed_indices {
3006 let node_index = (leaf_index + leaved_merkle_tree.num_leaves()) as u64;
3007 public_input.push(node_index.into());
3008 let revealed_leaf = leaved_merkle_tree.leaves_as_digests[leaf_index];
3009 public_input.extend(revealed_leaf.reversed().values());
3010 }
3011
3012 let program = crate::example_programs::MERKLE_TREE_AUTHENTICATION_PATH_VERIFY.clone();
3013 assert!(let Ok(_) = VM::run(program, public_input.into(), non_determinism));
3014 }
3015
3016 #[proptest]
3017 fn merkle_tree_updating_program_correctly_updates_a_merkle_tree(
3018 program_for_merkle_tree_update: ProgramForMerkleTreeUpdate,
3019 ) {
3020 prop_assert!(program_for_merkle_tree_update.is_integral());
3021 }
3022
3023 #[proptest(cases = 10)]
3024 fn prove_verify_merkle_tree_update(
3025 program_for_merkle_tree_update: ProgramForMerkleTreeUpdate,
3026 #[strategy(1_usize..=4)] log_2_fri_expansion_factor: usize,
3027 ) {
3028 let stark = Stark::new(Stark::LOW_SECURITY_LEVEL, log_2_fri_expansion_factor);
3029 program_for_merkle_tree_update
3030 .assemble()
3031 .use_stark(stark)
3032 .prove_and_verify();
3033 }
3034
3035 #[proptest]
3036 fn run_tvm_get_collinear_y(
3037 #[strategy(arb())] p0: (BFieldElement, BFieldElement),
3038 #[strategy(arb())]
3039 #[filter(#p0.0 != #p1.0)]
3040 p1: (BFieldElement, BFieldElement),
3041 #[strategy(arb())] p2_x: BFieldElement,
3042 ) {
3043 let p2_y = Polynomial::get_colinear_y(p0, p1, p2_x);
3044
3045 let get_collinear_y_program = triton_program!(
3046 read_io 5 swap 3 push -1 mul dup 1 add dup 3 push -1 mul dup 5 add mul dup 3 dup 3 push -1 mul add invert mul add swap 3 pop 3 write_io 1 halt
3053 );
3054
3055 let public_input = vec![p2_x, p1.1, p1.0, p0.1, p0.0];
3056 let_assert!(Ok(output) = VM::run(get_collinear_y_program, public_input.into(), [].into()));
3057 prop_assert_eq!(p2_y, output[0]);
3058 }
3059
3060 #[test]
3061 fn run_tvm_countdown_from_10() {
3062 let countdown_program = triton_program!(
3063 push 10
3064 call loop
3065
3066 loop:
3067 dup 0
3068 write_io 1
3069 push -1
3070 add
3071 dup 0
3072 skiz
3073 recurse
3074 write_io 1
3075 halt
3076 );
3077
3078 let_assert!(Ok(standard_out) = VM::run(countdown_program, [].into(), [].into()));
3079 let expected = (0..=10).map(BFieldElement::new).rev().collect_vec();
3080 assert!(expected == standard_out);
3081 }
3082
3083 #[test]
3084 fn run_tvm_fibonacci() {
3085 for (input, expected_output) in [(0, 1), (7, 21), (11, 144)] {
3086 let program = TestableProgram::new(crate::example_programs::FIBONACCI_SEQUENCE.clone())
3087 .with_input(bfe_array![input]);
3088 let_assert!(Ok(output) = program.run());
3089 let_assert!(&[output] = &output[..]);
3090 assert!(expected_output == output.value(), "input was: {input}");
3091 }
3092 }
3093
3094 #[test]
3095 fn run_tvm_swap() {
3096 let program = triton_program!(push 1 push 2 swap 1 assert write_io 1 halt);
3097 let_assert!(Ok(standard_out) = VM::run(program, [].into(), [].into()));
3098 assert!(bfe!(2) == standard_out[0]);
3099 }
3100
3101 #[test]
3102 fn swap_st0_is_like_no_op() {
3103 let program = triton_program!(push 42 swap 0 write_io 1 halt);
3104 let_assert!(Ok(standard_out) = VM::run(program, [].into(), [].into()));
3105 assert!(bfe!(42) == standard_out[0]);
3106 }
3107
3108 #[test]
3109 fn read_mem_uninitialized() {
3110 let program = triton_program!(read_mem 3 halt);
3111 let_assert!(Ok((aet, _)) = VM::trace_execution(program, [].into(), [].into()));
3112 assert!(2 == aet.processor_trace.nrows());
3113 }
3114
3115 #[test]
3116 fn read_non_deterministically_initialized_ram_at_address_0() {
3117 let program = triton_program!(push 0 read_mem 1 pop 1 write_io 1 halt);
3118
3119 let mut initial_ram = HashMap::new();
3120 initial_ram.insert(bfe!(0), bfe!(42));
3121 let non_determinism = NonDeterminism::default().with_ram(initial_ram);
3122 let program = TestableProgram::new(program).with_non_determinism(non_determinism);
3123
3124 let_assert!(Ok(public_output) = program.clone().run());
3125 let_assert!(&[output] = &public_output[..]);
3126 assert!(42 == output.value());
3127
3128 program.prove_and_verify();
3129 }
3130
3131 #[proptest(cases = 10)]
3132 fn read_non_deterministically_initialized_ram_at_random_address(
3133 #[strategy(arb())] uninitialized_address: BFieldElement,
3134 #[strategy(arb())]
3135 #[filter(#uninitialized_address != #initialized_address)]
3136 initialized_address: BFieldElement,
3137 #[strategy(arb())] value: BFieldElement,
3138 ) {
3139 let program = triton_program!(
3140 push {uninitialized_address} read_mem 1 pop 1 write_io 1
3141 push {initialized_address} read_mem 1 pop 1 write_io 1
3142 halt
3143 );
3144
3145 let mut initial_ram = HashMap::new();
3146 initial_ram.insert(initialized_address, value);
3147 let non_determinism = NonDeterminism::default().with_ram(initial_ram);
3148 let program = TestableProgram::new(program).with_non_determinism(non_determinism);
3149
3150 let_assert!(Ok(public_output) = program.clone().run());
3151 let_assert!(&[uninit_value, init_value] = &public_output[..]);
3152 assert!(0 == uninit_value.value());
3153 assert!(value == init_value);
3154
3155 program.prove_and_verify();
3156 }
3157
3158 #[test]
3159 fn program_without_halt() {
3160 let program = triton_program!(nop);
3161 let_assert!(Err(err) = VM::trace_execution(program, [].into(), [].into()));
3162 let_assert!(InstructionError::InstructionPointerOverflow = err.source);
3163 }
3164
3165 #[test]
3166 fn verify_sudoku() {
3167 let program = crate::example_programs::VERIFY_SUDOKU.clone();
3168 let sudoku = [
3169 8, 5, 9, 7, 6, 1, 4, 2, 3, 4, 2, 6, 8, 5, 3, 7, 9, 1, 7, 1, 3, 9, 2, 4, 8, 5, 6, 9, 6, 1, 5, 3, 7, 2, 8, 4, 2, 8, 7, 4, 1, 9, 6, 3, 5, 3, 4, 5, 2, 8, 6, 1, 7, 9, 5, 3, 4, 6, 7, 8, 9, 1, 2, 6, 7, 2, 1, 9, 5, 3, 4, 8, 1, 9, 8, 3, 4, 2, 5, 6, 7, ];
3181
3182 let std_in = PublicInput::from(sudoku.map(|b| bfe!(b)));
3183 let secret_in = NonDeterminism::default();
3184 assert!(let Ok(_) = VM::trace_execution(program.clone(), std_in, secret_in));
3185
3186 let bad_sudoku = [
3188 1, 2, 3, 4, 5, 7, 8, 9, 6, 4, 3, 1, 5, 2, 9, 6, 7, 8, 2, 7, 9, 6, 1, 3, 5, 8, 4, 7, 6, 5, 3, 4, 8, 9, 2, 1, 5, 1, 4, 9, 8, 6, 7, 3, 2, 6, 8, 2, 7, 9, 4, 1, 5, 3, 3, 5, 6, 8, 7, 2, 4, 1, 9, 9, 4, 8, 1, 3, 5, 2, 6, 7, 8, 9, 7, 2, 6, 1, 3, 4, 5, ];
3200 let bad_std_in = PublicInput::from(bad_sudoku.map(|b| bfe!(b)));
3201 let secret_in = NonDeterminism::default();
3202 let_assert!(Err(err) = VM::trace_execution(program, bad_std_in, secret_in));
3203 let_assert!(InstructionError::AssertionFailed(_) = err.source);
3204 }
3205
3206 fn instruction_does_not_change_vm_state_when_crashing_vm(
3207 program: TestableProgram,
3208 num_preparatory_steps: usize,
3209 ) {
3210 let mut vm_state = VMState::new(
3211 program.program,
3212 program.public_input,
3213 program.non_determinism,
3214 );
3215 for i in 0..num_preparatory_steps {
3216 assert!(let Ok(_) = vm_state.step(), "failed during step {i}");
3217 }
3218 let pre_crash_state = vm_state.clone();
3219 assert!(let Err(_) = vm_state.step());
3220 assert!(pre_crash_state == vm_state);
3221 }
3222
3223 #[test]
3224 fn instruction_pop_does_not_change_vm_state_when_crashing_vm() {
3225 let program = triton_program! { push 0 pop 2 halt };
3226 instruction_does_not_change_vm_state_when_crashing_vm(TestableProgram::new(program), 1);
3227 }
3228
3229 #[test]
3230 fn instruction_divine_does_not_change_vm_state_when_crashing_vm() {
3231 let program = triton_program! { divine 1 halt };
3232 instruction_does_not_change_vm_state_when_crashing_vm(TestableProgram::new(program), 0);
3233 }
3234
3235 #[test]
3236 fn instruction_assert_does_not_change_vm_state_when_crashing_vm() {
3237 let program = triton_program! { push 0 assert halt };
3238 instruction_does_not_change_vm_state_when_crashing_vm(TestableProgram::new(program), 1);
3239 }
3240
3241 #[test]
3242 fn instruction_merkle_step_does_not_change_vm_state_when_crashing_vm_invalid_node_index() {
3243 let non_u32 = u64::from(u32::MAX) + 1;
3244 let program = triton_program! { push {non_u32} swap 5 merkle_step halt };
3245 let nondeterminism = NonDeterminism::default().with_digests([Digest::default()]);
3246 let program = TestableProgram::new(program).with_non_determinism(nondeterminism);
3247 instruction_does_not_change_vm_state_when_crashing_vm(program, 2);
3248 }
3249
3250 #[test]
3251 fn instruction_merkle_step_does_not_change_vm_state_when_crashing_vm_no_nd_digests() {
3252 let valid_u32 = u64::from(u32::MAX);
3253 let program = triton_program! { push {valid_u32} swap 5 merkle_step halt };
3254 let program = TestableProgram::new(program);
3255 instruction_does_not_change_vm_state_when_crashing_vm(program, 2);
3256 }
3257
3258 #[test]
3259 fn instruction_merkle_step_mem_does_not_change_vm_state_when_crashing_vm_invalid_node_index() {
3260 let non_u32 = u64::from(u32::MAX) + 1;
3261 let program = triton_program! { push {non_u32} swap 5 merkle_step_mem halt };
3262 let program = TestableProgram::new(program);
3263 instruction_does_not_change_vm_state_when_crashing_vm(program, 2);
3264 }
3265
3266 #[test]
3267 fn instruction_assert_vector_does_not_change_vm_state_when_crashing_vm() {
3268 let program = triton_program! { push 0 push 1 push 0 push 0 push 0 assert_vector halt };
3269 instruction_does_not_change_vm_state_when_crashing_vm(TestableProgram::new(program), 5);
3270 }
3271
3272 #[test]
3273 fn instruction_sponge_absorb_does_not_change_vm_state_when_crashing_vm_sponge_uninit() {
3274 let ten_pushes = triton_asm![push 0; 10];
3275 let program = triton_program! { {&ten_pushes} sponge_absorb halt };
3276 instruction_does_not_change_vm_state_when_crashing_vm(TestableProgram::new(program), 10);
3277 }
3278
3279 #[test]
3280 fn instruction_sponge_absorb_does_not_change_vm_state_when_crashing_vm_stack_too_small() {
3281 let program = triton_program! { sponge_init sponge_absorb halt };
3282 instruction_does_not_change_vm_state_when_crashing_vm(TestableProgram::new(program), 1);
3283 }
3284
3285 #[test]
3286 fn instruction_sponge_absorb_mem_does_not_change_vm_state_when_crashing_vm_sponge_uninit() {
3287 let program = triton_program! { sponge_absorb_mem halt };
3288 instruction_does_not_change_vm_state_when_crashing_vm(TestableProgram::new(program), 0);
3289 }
3290
3291 #[test]
3292 fn instruction_sponge_squeeze_does_not_change_vm_state_when_crashing_vm() {
3293 let program = triton_program! { sponge_squeeze halt };
3294 instruction_does_not_change_vm_state_when_crashing_vm(TestableProgram::new(program), 0);
3295 }
3296
3297 #[test]
3298 fn instruction_invert_does_not_change_vm_state_when_crashing_vm() {
3299 let program = triton_program! { push 0 invert halt };
3300 instruction_does_not_change_vm_state_when_crashing_vm(TestableProgram::new(program), 1);
3301 }
3302
3303 #[test]
3304 fn instruction_lt_does_not_change_vm_state_when_crashing_vm() {
3305 let non_u32 = u64::from(u32::MAX) + 1;
3306 let program = triton_program! { push {non_u32} lt halt };
3307 instruction_does_not_change_vm_state_when_crashing_vm(TestableProgram::new(program), 1);
3308 }
3309
3310 #[test]
3311 fn instruction_and_does_not_change_vm_state_when_crashing_vm() {
3312 let non_u32 = u64::from(u32::MAX) + 1;
3313 let program = triton_program! { push {non_u32} and halt };
3314 instruction_does_not_change_vm_state_when_crashing_vm(TestableProgram::new(program), 1);
3315 }
3316
3317 #[test]
3318 fn instruction_xor_does_not_change_vm_state_when_crashing_vm() {
3319 let non_u32 = u64::from(u32::MAX) + 1;
3320 let program = triton_program! { push {non_u32} xor halt };
3321 instruction_does_not_change_vm_state_when_crashing_vm(TestableProgram::new(program), 1);
3322 }
3323
3324 #[test]
3325 fn instruction_log_2_floor_on_non_u32_operand_does_not_change_vm_state_when_crashing_vm() {
3326 let non_u32 = u64::from(u32::MAX) + 1;
3327 let program = triton_program! { push {non_u32} log_2_floor halt };
3328 instruction_does_not_change_vm_state_when_crashing_vm(TestableProgram::new(program), 1);
3329 }
3330
3331 #[test]
3332 fn instruction_log_2_floor_on_operand_0_does_not_change_vm_state_when_crashing_vm() {
3333 let program = triton_program! { push 0 log_2_floor halt };
3334 instruction_does_not_change_vm_state_when_crashing_vm(TestableProgram::new(program), 1);
3335 }
3336
3337 #[test]
3338 fn instruction_pow_does_not_change_vm_state_when_crashing_vm() {
3339 let non_u32 = u64::from(u32::MAX) + 1;
3340 let program = triton_program! { push {non_u32} push 0 pow halt };
3341 instruction_does_not_change_vm_state_when_crashing_vm(TestableProgram::new(program), 2);
3342 }
3343
3344 #[test]
3345 fn instruction_div_mod_on_non_u32_operand_does_not_change_vm_state_when_crashing_vm() {
3346 let non_u32 = u64::from(u32::MAX) + 1;
3347 let program = triton_program! { push {non_u32} push 0 div_mod halt };
3348 instruction_does_not_change_vm_state_when_crashing_vm(TestableProgram::new(program), 2);
3349 }
3350
3351 #[test]
3352 fn instruction_div_mod_on_denominator_0_does_not_change_vm_state_when_crashing_vm() {
3353 let program = triton_program! { push 0 push 1 div_mod halt };
3354 instruction_does_not_change_vm_state_when_crashing_vm(TestableProgram::new(program), 2);
3355 }
3356
3357 #[test]
3358 fn instruction_pop_count_does_not_change_vm_state_when_crashing_vm() {
3359 let non_u32 = u64::from(u32::MAX) + 1;
3360 let program = triton_program! { push {non_u32} pop_count halt };
3361 instruction_does_not_change_vm_state_when_crashing_vm(TestableProgram::new(program), 1);
3362 }
3363
3364 #[test]
3365 fn instruction_x_invert_does_not_change_vm_state_when_crashing_vm() {
3366 let program = triton_program! { x_invert halt };
3367 instruction_does_not_change_vm_state_when_crashing_vm(TestableProgram::new(program), 0);
3368 }
3369
3370 #[test]
3371 fn instruction_read_io_does_not_change_vm_state_when_crashing_vm() {
3372 let program = triton_program! { read_io 1 halt };
3373 instruction_does_not_change_vm_state_when_crashing_vm(TestableProgram::new(program), 0);
3374 }
3375
3376 #[proptest]
3377 fn serialize_deserialize_vm_state_to_and_from_json_is_identity(
3378 #[strategy(arb())] vm_state: VMState,
3379 ) {
3380 let serialized = serde_json::to_string(&vm_state).unwrap();
3381 let deserialized = serde_json::from_str(&serialized).unwrap();
3382 prop_assert_eq!(vm_state, deserialized);
3383 }
3384
3385 #[proptest]
3386 fn xx_dot_step(
3387 #[strategy(0_usize..=25)] n: usize,
3388 #[strategy(vec(arb(), #n))] lhs: Vec<XFieldElement>,
3389 #[strategy(vec(arb(), #n))] rhs: Vec<XFieldElement>,
3390 #[strategy(arb())] lhs_address: BFieldElement,
3391 #[strategy(arb())] rhs_address: BFieldElement,
3392 ) {
3393 let mem_region_size = (n * EXTENSION_DEGREE) as u64;
3394 let mem_region = |addr: BFieldElement| addr.value()..addr.value() + mem_region_size;
3395 let right_in_left = mem_region(lhs_address).contains(&rhs_address.value());
3396 let left_in_right = mem_region(rhs_address).contains(&lhs_address.value());
3397 if right_in_left || left_in_right {
3398 let reason = "storing into overlapping regions would overwrite";
3399 return Err(TestCaseError::Reject(reason.into()));
3400 }
3401
3402 let lhs_flat = lhs.iter().flat_map(|&xfe| xfe.coefficients).collect_vec();
3403 let rhs_flat = rhs.iter().flat_map(|&xfe| xfe.coefficients).collect_vec();
3404 let mut ram = HashMap::new();
3405 for (i, &l, &r) in izip!(0.., &lhs_flat, &rhs_flat) {
3406 ram.insert(lhs_address + bfe!(i), l);
3407 ram.insert(rhs_address + bfe!(i), r);
3408 }
3409
3410 let public_input = PublicInput::default();
3411 let secret_input = NonDeterminism::default().with_ram(ram);
3412 let many_xx_dot_steps = triton_asm![xx_dot_step; n];
3413 let program = triton_program! {
3414 push 0 push 0 push 0
3415
3416 push {lhs_address}
3417 push {rhs_address}
3418
3419 {&many_xx_dot_steps}
3420 halt
3421 };
3422
3423 let mut vmstate = VMState::new(program, public_input, secret_input);
3424 prop_assert!(vmstate.run().is_ok());
3425
3426 prop_assert_eq!(
3427 vmstate.op_stack.pop().unwrap(),
3428 rhs_address + bfe!(rhs_flat.len() as u64)
3429 );
3430 prop_assert_eq!(
3431 vmstate.op_stack.pop().unwrap(),
3432 lhs_address + bfe!(lhs_flat.len() as u64)
3433 );
3434
3435 let observed_dot_product = vmstate.op_stack.pop_extension_field_element().unwrap();
3436 let expected_dot_product = lhs
3437 .into_iter()
3438 .zip(rhs)
3439 .map(|(l, r)| l * r)
3440 .sum::<XFieldElement>();
3441 prop_assert_eq!(expected_dot_product, observed_dot_product);
3442 }
3443
3444 #[proptest]
3445 fn xb_dot_step(
3446 #[strategy(0_usize..=25)] n: usize,
3447 #[strategy(vec(arb(), #n))] lhs: Vec<XFieldElement>,
3448 #[strategy(vec(arb(), #n))] rhs: Vec<BFieldElement>,
3449 #[strategy(arb())] lhs_address: BFieldElement,
3450 #[strategy(arb())] rhs_address: BFieldElement,
3451 ) {
3452 let mem_region_size_lhs = (n * EXTENSION_DEGREE) as u64;
3453 let mem_region_lhs = lhs_address.value()..lhs_address.value() + mem_region_size_lhs;
3454 let mem_region_rhs = rhs_address.value()..rhs_address.value() + n as u64;
3455 let right_in_left = mem_region_lhs.contains(&rhs_address.value());
3456 let left_in_right = mem_region_rhs.contains(&lhs_address.value());
3457 if right_in_left || left_in_right {
3458 let reason = "storing into overlapping regions would overwrite";
3459 return Err(TestCaseError::Reject(reason.into()));
3460 }
3461
3462 let lhs_flat = lhs.iter().flat_map(|&xfe| xfe.coefficients).collect_vec();
3463 let mut ram = HashMap::new();
3464 for (i, &l) in (0..).zip(&lhs_flat) {
3465 ram.insert(lhs_address + bfe!(i), l);
3466 }
3467 for (i, &r) in (0..).zip(&rhs) {
3468 ram.insert(rhs_address + bfe!(i), r);
3469 }
3470
3471 let public_input = PublicInput::default();
3472 let secret_input = NonDeterminism::default().with_ram(ram);
3473 let many_xb_dot_steps = triton_asm![xb_dot_step; n];
3474 let program = triton_program! {
3475 push 0 push 0 push 0
3476
3477 push {lhs_address}
3478 push {rhs_address}
3479
3480 {&many_xb_dot_steps}
3481 halt
3482 };
3483
3484 let mut vmstate = VMState::new(program, public_input, secret_input);
3485 prop_assert!(vmstate.run().is_ok());
3486
3487 prop_assert_eq!(
3488 vmstate.op_stack.pop().unwrap(),
3489 rhs_address + bfe!(rhs.len() as u64)
3490 );
3491 prop_assert_eq!(
3492 vmstate.op_stack.pop().unwrap(),
3493 lhs_address + bfe!(lhs_flat.len() as u64)
3494 );
3495 let observed_dot_product = vmstate.op_stack.pop_extension_field_element().unwrap();
3496 let expected_dot_product = lhs
3497 .into_iter()
3498 .zip(rhs)
3499 .map(|(l, r)| l * r)
3500 .sum::<XFieldElement>();
3501 prop_assert_eq!(expected_dot_product, observed_dot_product);
3502 }
3503
3504 #[test]
3505 fn iterating_over_public_inputs_individual_tokens_is_easy() {
3506 let public_input = PublicInput::from(bfe_vec![1, 2, 3]);
3507 let actual = public_input.iter().join(", ");
3508 assert_eq!("1, 2, 3", actual);
3509 }
3510}