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