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