triton_vm/
vm.rs

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    /// The **program memory** stores the instructions (and their arguments) of the program
53    /// currently being executed by Triton VM. It is read-only.
54    pub program: Program,
55
56    /// A list of [`BFieldElement`]s the program can read from using instruction `read_io`.
57    pub public_input: VecDeque<BFieldElement>,
58
59    /// A list of [`BFieldElement`]s the program can write to using instruction `write_io`.
60    pub public_output: Vec<BFieldElement>,
61
62    /// A list of [`BFieldElement`]s the program can read from using instruction `divine`.
63    pub secret_individual_tokens: VecDeque<BFieldElement>,
64
65    /// A list of [`Digest`]s the program can use for instruction `merkle_step`.
66    pub secret_digests: VecDeque<Digest>,
67
68    /// The read-write **random-access memory** allows Triton VM to store arbitrary data.
69    pub ram: HashMap<BFieldElement, BFieldElement>,
70
71    ram_calls: Vec<RamTableCall>,
72
73    /// The **Op-stack memory** stores Triton VM's entire operational stack.
74    pub op_stack: OpStack,
75
76    /// The **Jump-stack memory** stores the entire jump stack.
77    pub jump_stack: Vec<(BFieldElement, BFieldElement)>,
78
79    /// Number of cycles the program has been running for
80    pub cycle_count: u32,
81
82    /// Current instruction's address in program memory
83    pub instruction_pointer: usize,
84
85    /// The current state of the one, global [`Sponge`] that can be manipulated
86    /// using instructions [`SpongeInit`][init], [`SpongeAbsorb`][absorb],
87    /// [`SpongeAbsorbMem`][absorb_mem], and [`SpongeSqueeze`][squeeze].
88    /// Instruction [`SpongeInit`][init] resets the Sponge.
89    ///
90    /// Note that this is the _full_ state, including capacity. The capacity should never be
91    /// exposed outside the VM.
92    ///
93    /// [init]: Instruction::SpongeInit
94    /// [absorb]: Instruction::SpongeAbsorb
95    /// [absorb_mem]: Instruction::SpongeAbsorbMem
96    /// [squeeze]: Instruction::SpongeSqueeze
97    pub sponge: Option<Tip5>,
98
99    /// Indicates whether the terminating instruction `halt` has been executed.
100    pub halting: bool,
101}
102
103/// A call from the main processor to one of the coprocessors, including the trace for that
104/// coprocessor or enough information to deduce the trace.
105#[derive(Debug, Clone, Eq, PartialEq)]
106pub enum CoProcessorCall {
107    SpongeStateReset,
108
109    /// Trace of the state registers for hash coprocessor table when executing
110    /// instruction `hash` or one of the Sponge instructions `sponge_absorb`,
111    /// `sponge_absorb_mem`, and `sponge_squeeze`.
112    ///
113    /// One row per round in the Tip5 permutation.
114    Tip5Trace(Instruction, Box<PermutationTrace>),
115
116    U32(U32TableEntry),
117
118    OpStack(OpStackTableEntry),
119
120    Ram(RamTableCall),
121}
122
123impl VM {
124    /// Run Triton VM on the [`Program`] with the given public input and non-determinism.
125    /// If an error is encountered, the returned [`VMError`] contains the [`VMState`] at the point
126    /// of execution failure.
127    ///
128    /// See also [`trace_execution`][trace_execution] and [`profile`][profile].
129    ///
130    /// [trace_execution]: Self::trace_execution
131    /// [profile]: Self::profile
132    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    /// Trace the execution of a [`Program`]. That is, [`run`][run] the [`Program`] and additionally
145    /// record that part of every encountered state that is necessary for proving correct execution.
146    /// If execution  succeeds, returns
147    /// 1. an [`AlgebraicExecutionTrace`], and
148    /// 1. the output of the program.
149    ///
150    /// See also [`run`][run] and [`profile`][profile].
151    ///
152    /// [run]: Self::run
153    /// [profile]: Self::profile
154    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    /// Trace the execution of a [`Program`] from a given [`VMState`]. Consider
167    /// using [`trace_execution`][Self::trace_execution], unless you know this is
168    /// what you want.
169    ///
170    /// Returns the [`AlgebraicExecutionTrace`] and the terminal [`VMState`] if
171    /// execution succeeds.
172    ///
173    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    /// Run Triton VM with the given public and secret input, recording the
195    /// influence of a callable block of instructions on the
196    /// [`AlgebraicExecutionTrace`]. For example, this can be used to identify the
197    /// number of clock cycles spent in some block of instructions, or how many rows
198    /// it contributes to the U32 Table.
199    ///
200    /// See also [`run`][run] and [`trace_execution`][trace_execution].
201    ///
202    /// [run]: Self::run
203    /// [trace_execution]: Self::trace_execution
204    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    /// Create initial `VMState` for a given `program`.
235    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    /// Perform the state transition as a mutable operation on `self`.
357    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            // there are not enough helper variables – overwrite part of the stack :(
707            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        // Triton VM uses the following equality to compute the results of both the `and`
856        // and `xor` instruction using the u32 coprocessor's `and` capability:
857        // a ^ b = a + b - 2 · (a & b)
858        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    /// The “next instruction or argument” (NIA) is
1157    /// - the argument of the current instruction if it has one, or
1158    /// - the opcode of the next instruction otherwise.
1159    ///
1160    /// If the current instruction has no argument and there is no next instruction, the NIA is 1
1161    /// to account for the hash-input padding separator of the program.
1162    ///
1163    /// If the instruction pointer is out of bounds, the returned NIA is 0.
1164    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    /// Return the next instruction on the tape, skipping arguments.
1198    ///
1199    /// Note that this is not necessarily the next instruction to execute, since the
1200    /// current instruction could be a jump, but it is either
1201    /// `program.instructions[ip + 1]` or `program.instructions[ip + 2]`, depending
1202    /// on whether the current instruction takes an argument.
1203    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    /// Run Triton VM on this state to completion, or until an error occurs.
1233    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 `: `
1381
1382            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/// All sources of non-determinism for a program. This includes elements that
1449/// can be read using instruction `divine`, digests that can be read using
1450/// instruction `merkle_step`, and an initial state of random-access memory.
1451#[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            // need jump stack setup
1573            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    /// Test helper for property testing instruction `recurse_or_return`.
1813    ///
1814    /// The [assembled](Self::assemble) program
1815    /// - sets up a loop counter,
1816    /// - populates ST6 with some “iteration terminator”,
1817    /// - reads successive elements from standard input, and
1818    /// - compares them to the iteration terminator using `recurse_or_return`.
1819    ///
1820    /// The program halts after the loop has run for the expected number of
1821    /// iterations, crashing the VM if the number of iterations does not match
1822    /// expectations.
1823    #[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                // set up iteration counter
1839                push 0 hint iteration_counter = stack[0]
1840
1841                // set up termination condition
1842                push {self.iteration_terminator}
1843                swap 6
1844
1845                call iteration_loop
1846
1847                // check iteration counter
1848                push {expected_num_iterations}
1849                eq assert
1850                halt
1851
1852                iteration_loop:
1853                    // increment iteration counter
1854                    push 1 add
1855
1856                    // check loop termination
1857                    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    // _ 3 1 2
1885            push 7                  // _ 3 1 2 7
1886            write_mem 3             // _ 10
1887            push -1 add             // _ 9
1888            read_mem 2              // _ 3 1 7
1889            pop 1                   // _ 3 1
1890            assert halt             // _ 3
1891        })
1892    }
1893
1894    pub(crate) fn test_program_for_hash() -> ProgramAndInput {
1895        let program = triton_program!(
1896            push 0 // filler to keep the OpStack large enough throughout the program
1897            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    /// Helper function that returns code to push a digest to the top of the stack
1907    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 // dummy
1958            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 // dummy
1984            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    /// Test helper for property-testing instruction `merkle_step_mem` in a
2002    /// meaningful context, namely, using [`MERKLE_TREE_UPDATE`].
2003    #[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        /// Checks whether the [`ProgramAndInput`] generated through [`Self::assemble`]
2047        /// can
2048        /// - be executed without crashing the VM, and
2049        /// - produces the correct output.
2050        #[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        /// The authentication path for the leaf at `self.revealed_leafs_index`.
2064        /// Independent of the leaf's value, _i.e._, is up to date even one the leaf
2065        /// has been updated.
2066        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! {         //  elements on stack
2108            sponge_init                         //  0
2109            sponge_squeeze sponge_absorb        //  0
2110            {&push_10_zeros} sponge_absorb      //  0
2111            {&push_10_zeros} sponge_absorb      //  0
2112            sponge_squeeze sponge_squeeze       // 20
2113            sponge_squeeze sponge_absorb        // 20
2114            sponge_init sponge_init sponge_init // 20
2115            sponge_absorb                       // 10
2116            sponge_init                         // 10
2117            sponge_squeeze sponge_squeeze       // 30
2118            sponge_init sponge_squeeze          // 40
2119            {&push_5_zeros} hash sponge_absorb  // 30
2120            {&push_5_zeros} hash sponge_squeeze // 40
2121            {&push_5_zeros} hash sponge_absorb  // 30
2122            {&push_5_zeros} hash sponge_squeeze // 40
2123            sponge_init                         // 40
2124            sponge_absorb sponge_absorb         // 20
2125            sponge_absorb sponge_absorb         //  0
2126            {&push_10_zeros} sponge_absorb      //  0
2127            {&push_10_zeros} sponge_absorb      //  0
2128            {&push_10_zeros} sponge_absorb      //  0
2129            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    /// Test helper for [`ProgramForSpongeAndHashInstructions`].
2147    #[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(_) => (), // no effect on Sponge
2187            }
2188        }
2189    }
2190
2191    /// Test helper for arbitrary sequences of hashing-related instructions.
2192    #[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 <exponent: u32> push <base: BFE> pow push <result: BFE> eq assert
2418            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        // Read some memory before first write to ensure that the memory is initialized with 0s.
2516        // Not all addresses are read to have different access patterns:
2517        // - Some addresses are read before written to.
2518        // - Other addresses are written to before read.
2519        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        // Write everything to RAM.
2524        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        // Read back in random order and check that the values did not change.
2529        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        // Overwrite half the values with new ones.
2542        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        // Read back all, i.e., unchanged and overwritten values in (different from before) random
2556        // order and check that the values did not change.
2557        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    /// Sanity check for the relatively complex property-based test for random RAM access.
2574    #[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    /// Sanity check
2645    #[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    /// Sanity check
2719    #[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  // _ [own_digest]
2788            push 4 read_mem 5 pop 1             // _ [own_digest] [claim_digest]
2789            assert_vector                       // _ [own_digest]
2790            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    // compile-time assertion
2897    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 // \n
2903            push  33 // !
2904            push 100 // d
2905            push 108 // l
2906            push 114 // r
2907            push 111 // o
2908            push  87 // W
2909            push  32 //
2910            push  44 // ,
2911            push 111 // o
2912            push 108 // l
2913            push 108 // l
2914            push 101 // e
2915            push  72 // H
2916            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   // write  8 to address  5
2967            push 18 push 15 write_mem 1 pop 1   // write 18 to address 15
2968            push  5         read_mem  1 pop 2   // read from address  5
2969            push 15         read_mem  1 pop 2   // read from address 15
2970            push  7 push  5 write_mem 1 pop 1   // write  7 to address  5
2971            push 15         read_mem  1         // _ 18 14
2972            push  5         read_mem  1         // _ 18 14 7 4
2973            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                        //       ┌ stack cannot shrink beyond this point
2988                        //       ↓
2989                        // _ 0 0 |
2990            push 0      // _ 0 0 | 0
2991            write_mem 1 // _ 0 1 |
2992            push 5      // _ 0 1 | 5
2993            swap 1      // _ 0 5 | 1
2994            push 3      // _ 0 5 | 1 3
2995            swap 1      // _ 0 5 | 3 1
2996            pop 1       // _ 0 5 | 3
2997            write_mem 1 // _ 0 4 |
2998            push -1 add // _ 0 3 |
2999            read_mem 1  // _ 0 5 | 2
3000            swap 2      // _ 2 5 | 0
3001            pop 1       // _ 2 5 |
3002            swap 1      // _ 5 2 |
3003            push 1      // _ 5 2 | 1
3004            add         // _ 5 3 |
3005            read_mem 1  // _ 5 5 | 2
3006            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                       // p0 p1 p2_x
3072            swap 3 push -1 mul dup 1 add    // dy = p0_y - p1_y
3073            dup 3 push -1 mul dup 5 add mul // dy·(p2_x - p0_x)
3074            dup 3 dup 3 push -1 mul add     // dx = p0_x - p1_x
3075            invert mul add                  // compute result
3076            swap 3 pop 3                    // leave a clean stack
3077            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, //
3202            4, 2, 6, /**/ 8, 5, 3, /**/ 7, 9, 1, //
3203            7, 1, 3, /**/ 9, 2, 4, /**/ 8, 5, 6, //
3204            /*************************************/
3205            9, 6, 1, /**/ 5, 3, 7, /**/ 2, 8, 4, //
3206            2, 8, 7, /**/ 4, 1, 9, /**/ 6, 3, 5, //
3207            3, 4, 5, /**/ 2, 8, 6, /**/ 1, 7, 9, //
3208            /*************************************/
3209            5, 3, 4, /**/ 6, 7, 8, /**/ 9, 1, 2, //
3210            6, 7, 2, /**/ 1, 9, 5, /**/ 3, 4, 8, //
3211            1, 9, 8, /**/ 3, 4, 2, /**/ 5, 6, 7, //
3212        ];
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        // rows and columns adhere to Sudoku rules, boxes do not
3219        let bad_sudoku = [
3220            1, 2, 3, /**/ 4, 5, 7, /**/ 8, 9, 6, //
3221            4, 3, 1, /**/ 5, 2, 9, /**/ 6, 7, 8, //
3222            2, 7, 9, /**/ 6, 1, 3, /**/ 5, 8, 4, //
3223            /*************************************/
3224            7, 6, 5, /**/ 3, 4, 8, /**/ 9, 2, 1, //
3225            5, 1, 4, /**/ 9, 8, 6, /**/ 7, 3, 2, //
3226            6, 8, 2, /**/ 7, 9, 4, /**/ 1, 5, 3, //
3227            /*************************************/
3228            3, 5, 6, /**/ 8, 7, 2, /**/ 4, 1, 9, //
3229            9, 4, 8, /**/ 1, 3, 5, /**/ 2, 6, 7, //
3230            8, 9, 7, /**/ 2, 6, 1, /**/ 3, 4, 5, //
3231        ];
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}