• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TritonVM / triton-vm / 10825170249

12 Sep 2024 06:08AM UTC coverage: 98.369%. Remained the same
10825170249

push

github

jan-ferdinand
chore: Use “main” & “aux over “base” & “ext”

This completes the transition started in 07fdf159.

changelog: ignore

546 of 558 new or added lines in 17 files covered. (97.85%)

30 existing lines in 4 files now uncovered.

23038 of 23420 relevant lines covered (98.37%)

7954849.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.41
/triton-vm/src/vm.rs
1
use std::collections::HashMap;
2
use std::collections::VecDeque;
3
use std::fmt::Display;
4
use std::fmt::Formatter;
5
use std::fmt::Result as FmtResult;
6
use std::ops::Range;
7

8
use air::table::hash::PermutationTrace;
9
use air::table::processor::ProcessorTable;
10
use air::table::processor::NUM_HELPER_VARIABLE_REGISTERS;
11
use air::table_column::*;
12
use air::AIR;
13
use arbitrary::Arbitrary;
14
use isa::error::InstructionError;
15
use isa::instruction::AnInstruction::*;
16
use isa::instruction::Instruction;
17
use isa::op_stack::OpStackElement::*;
18
use isa::op_stack::*;
19
use isa::program::Program;
20
use itertools::Itertools;
21
use ndarray::Array1;
22
use num_traits::ConstZero;
23
use num_traits::One;
24
use num_traits::Zero;
25
use serde::Deserialize;
26
use serde::Serialize;
27
use strum::EnumCount;
28
use twenty_first::math::x_field_element::EXTENSION_DEGREE;
29
use twenty_first::prelude::*;
30
use twenty_first::util_types::algebraic_hasher::Domain;
31

32
use crate::aet::AlgebraicExecutionTrace;
33
use crate::error::VMError;
34
use crate::execution_trace_profiler::ExecutionTraceProfile;
35
use crate::execution_trace_profiler::ExecutionTraceProfiler;
36
use crate::profiler::profiler;
37
use crate::table::op_stack::OpStackTableEntry;
38
use crate::table::ram::RamTableCall;
39
use crate::table::u32::U32TableEntry;
40
use crate::vm::CoProcessorCall::*;
41

42
type VMResult<T> = Result<T, VMError>;
43
type InstructionResult<T> = Result<T, InstructionError>;
44

45
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize, Arbitrary)]
×
46
pub struct VM;
47

48
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Arbitrary)]
3,584✔
49
pub struct VMState {
50
    /// The **program memory** stores the instructions (and their arguments) of the program
51
    /// currently being executed by Triton VM. It is read-only.
52
    pub program: Vec<Instruction>,
53

54
    /// A list of [`BFieldElement`]s the program can read from using instruction `read_io`.
55
    pub public_input: VecDeque<BFieldElement>,
56

57
    /// A list of [`BFieldElement`]s the program can write to using instruction `write_io`.
58
    pub public_output: Vec<BFieldElement>,
59

60
    /// A list of [`BFieldElement`]s the program can read from using instruction `divine`.
61
    pub secret_individual_tokens: VecDeque<BFieldElement>,
62

63
    /// A list of [`Digest`]s the program can use for instruction `merkle_step`.
64
    pub secret_digests: VecDeque<Digest>,
65

66
    /// The read-write **random-access memory** allows Triton VM to store arbitrary data.
67
    pub ram: HashMap<BFieldElement, BFieldElement>,
68

69
    ram_calls: Vec<RamTableCall>,
70

71
    /// The **Op-stack memory** stores Triton VM's entire operational stack.
72
    pub op_stack: OpStack,
73

74
    /// The **Jump-stack memory** stores the entire jump stack.
75
    pub jump_stack: Vec<(BFieldElement, BFieldElement)>,
76

77
    /// Number of cycles the program has been running for
78
    pub cycle_count: u32,
79

80
    /// Current instruction's address in program memory
81
    pub instruction_pointer: usize,
82

83
    /// The current state of the one, global [`Sponge`] that can be manipulated
84
    /// using instructions [`SpongeInit`], [`SpongeAbsorb`], [`SpongeAbsorbMem`],
85
    /// and [`SpongeSqueeze`]. Instruction [`SpongeInit`] resets the Sponge.
86
    ///
87
    /// Note that this is the _full_ state, including capacity. The capacity should never be
88
    /// exposed outside the VM.
89
    pub sponge: Option<Tip5>,
90

91
    /// Indicates whether the terminating instruction `halt` has been executed.
92
    pub halting: bool,
93
}
94

95
/// A call from the main processor to one of the co-processors, including the trace for that
96
/// co-processor or enough information to deduce the trace.
97
#[derive(Debug, Clone, Eq, PartialEq)]
98
pub enum CoProcessorCall {
99
    SpongeStateReset,
100

101
    /// Trace of the state registers for hash coprocessor table when executing
102
    /// instruction `hash` or one of the Sponge instructions `sponge_absorb`,
103
    /// `sponge_absorb_mem`, and `sponge_squeeze`.
104
    ///
105
    /// One row per round in the Tip5 permutation.
106
    Tip5Trace(Instruction, Box<PermutationTrace>),
107

108
    U32Call(U32TableEntry),
109

110
    OpStackCall(OpStackTableEntry),
111

112
    RamCall(RamTableCall),
113
}
114

115
impl VM {
116
    /// Run Triton VM on the [`Program`] with the given public input and non-determinism.
117
    /// If an error is encountered, the returned [`VMError`] contains the [`VMState`] at the point
118
    /// of execution failure.
119
    ///
120
    /// See also [`trace_execution`][trace_execution] and [`profile`][profile].
121
    ///
122
    /// [trace_execution]: Self::trace_execution
123
    /// [profile]: Self::profile
124
    pub fn run(
3,376✔
125
        program: &Program,
3,376✔
126
        public_input: PublicInput,
3,376✔
127
        non_determinism: NonDeterminism,
3,376✔
128
    ) -> VMResult<Vec<BFieldElement>> {
3,376✔
129
        let mut state = VMState::new(program, public_input, non_determinism);
3,376✔
130
        if let Err(err) = state.run() {
3,376✔
131
            return Err(VMError::new(err, state));
784✔
132
        }
2,592✔
133
        Ok(state.public_output)
2,592✔
134
    }
3,376✔
135

136
    /// Trace the execution of a [`Program`]. That is, [`run`][run] the [`Program`] and additionally
137
    /// record that part of every encountered state that is necessary for proving correct execution.
138
    /// If execution  succeeds, returns
139
    /// 1. an [`AlgebraicExecutionTrace`], and
140
    /// 1. the output of the program.
141
    ///
142
    /// See also [`run`][run] and [`profile`][profile].
143
    ///
144
    /// [run]: Self::run
145
    /// [profile]: Self::profile
146
    pub fn trace_execution(
681✔
147
        program: &Program,
681✔
148
        public_input: PublicInput,
681✔
149
        non_determinism: NonDeterminism,
681✔
150
    ) -> VMResult<(AlgebraicExecutionTrace, Vec<BFieldElement>)> {
681✔
151
        profiler!(start "trace execution" ("gen"));
681✔
152
        let state = VMState::new(program, public_input, non_determinism);
681✔
153
        let (aet, terminal_state) = Self::trace_execution_of_state(program, state)?;
681✔
154
        profiler!(stop "trace execution");
679✔
155
        Ok((aet, terminal_state.public_output))
679✔
156
    }
681✔
157

158
    /// Trace the execution of a [`Program`] from a given [`VMState`]. Consider
159
    /// using [`trace_execution`][Self::trace_execution], unless you know this is
160
    /// what you want.
161
    ///
162
    /// Returns the [`AlgebraicExecutionTrace`] and the terminal [`VMState`] if
163
    /// execution succeeds.
164
    ///
165
    /// # Panics
166
    ///
167
    /// - if the given [`VMState`] is not about to `self`
168
    /// - if the given [`VMState`] is incorrectly initialized
169
    pub fn trace_execution_of_state(
681✔
170
        program: &Program,
681✔
171
        mut state: VMState,
681✔
172
    ) -> VMResult<(AlgebraicExecutionTrace, VMState)> {
681✔
173
        let mut aet = AlgebraicExecutionTrace::new(program.clone());
681✔
174
        assert_eq!(program.instructions, state.program);
681✔
175
        assert_eq!(program.len_bwords(), aet.instruction_multiplicities.len());
681✔
176

177
        while !state.halting {
228,009✔
178
            if let Err(err) = aet.record_state(&state) {
227,330✔
179
                return Err(VMError::new(err, state));
1✔
180
            };
227,329✔
181
            let co_processor_calls = match state.step() {
227,329✔
182
                Ok(calls) => calls,
227,328✔
183
                Err(err) => return Err(VMError::new(err, state)),
1✔
184
            };
185
            for call in co_processor_calls {
494,649✔
186
                aet.record_co_processor_call(call);
267,321✔
187
            }
267,321✔
188
        }
189

190
        Ok((aet, state))
679✔
191
    }
681✔
192

193
    /// Run Triton VM with the given public and secret input, recording the
194
    /// influence of a callable block of instructions on the
195
    /// [`AlgebraicExecutionTrace`]. For example, this can be used to identify the
196
    /// number of clock cycles spent in some block of instructions, or how many rows
197
    /// it contributes to the U32 Table.
198
    ///
199
    /// See also [`run`][run] and [`trace_execution`][trace_execution].
200
    ///
201
    /// [run]: Self::run
202
    /// [trace_execution]: Self::trace_execution
203
    pub fn profile(
2✔
204
        program: &Program,
2✔
205
        public_input: PublicInput,
2✔
206
        non_determinism: NonDeterminism,
2✔
207
    ) -> VMResult<(Vec<BFieldElement>, ExecutionTraceProfile)> {
2✔
208
        let mut profiler = ExecutionTraceProfiler::new(program.instructions.len());
2✔
209
        let mut state = VMState::new(program, public_input, non_determinism);
2✔
210
        let mut previous_jump_stack_len = state.jump_stack.len();
2✔
211
        while !state.halting {
408✔
212
            if let Ok(Instruction::Call(address)) = state.current_instruction() {
407✔
213
                let label = program.label_for_address(address.value());
21✔
214
                profiler.enter_span(label);
21✔
215
            }
386✔
216

217
            match state.step() {
407✔
218
                Ok(calls) => profiler.handle_co_processor_calls(calls),
406✔
219
                Err(err) => return Err(VMError::new(err, state)),
1✔
220
            };
221

222
            if state.jump_stack.len() < previous_jump_stack_len {
406✔
223
                profiler.exit_span();
21✔
224
            }
385✔
225
            previous_jump_stack_len = state.jump_stack.len();
406✔
226
        }
227

228
        Ok((state.public_output, profiler.finish()))
1✔
229
    }
2✔
230
}
231

232
impl VMState {
233
    /// Create initial `VMState` for a given `program`
234
    ///
235
    /// Since `program` is read-only across individual states, and multiple
236
    /// inner helper functions refer to it, a read-only reference is kept in
237
    /// the struct.
238
    pub fn new(
5,410✔
239
        program: &Program,
5,410✔
240
        public_input: PublicInput,
5,410✔
241
        non_determinism: NonDeterminism,
5,410✔
242
    ) -> Self {
5,410✔
243
        let program_digest = program.hash();
5,410✔
244

5,410✔
245
        Self {
5,410✔
246
            program: program.instructions.clone(),
5,410✔
247
            public_input: public_input.individual_tokens.into(),
5,410✔
248
            public_output: vec![],
5,410✔
249
            secret_individual_tokens: non_determinism.individual_tokens.into(),
5,410✔
250
            secret_digests: non_determinism.digests.into(),
5,410✔
251
            ram: non_determinism.ram,
5,410✔
252
            ram_calls: vec![],
5,410✔
253
            op_stack: OpStack::new(program_digest),
5,410✔
254
            jump_stack: vec![],
5,410✔
255
            cycle_count: 0,
5,410✔
256
            instruction_pointer: 0,
5,410✔
257
            sponge: None,
5,410✔
258
            halting: false,
5,410✔
259
        }
5,410✔
260
    }
5,410✔
261

262
    pub fn derive_helper_variables(&self) -> [BFieldElement; NUM_HELPER_VARIABLE_REGISTERS] {
227,331✔
263
        let mut hvs = bfe_array![0; NUM_HELPER_VARIABLE_REGISTERS];
227,331✔
264
        let Ok(current_instruction) = self.current_instruction() else {
227,331✔
265
            return hvs;
×
266
        };
267

268
        let decompose_arg = |a: u64| bfe_array![a % 2, (a >> 1) % 2, (a >> 2) % 2, (a >> 3) % 2];
227,331✔
269
        let ram_read = |address| self.ram.get(&address).copied().unwrap_or_else(|| bfe!(0));
227,331✔
270

271
        match current_instruction {
227,331✔
272
            Pop(_) | Divine(_) | Dup(_) | Swap(_) | ReadMem(_) | WriteMem(_) | ReadIo(_)
273
            | WriteIo(_) => {
80,068✔
274
                let arg = current_instruction.arg().unwrap().value();
80,068✔
275
                hvs[..4].copy_from_slice(&decompose_arg(arg));
80,068✔
276
            }
80,068✔
277
            Skiz => {
15,298✔
278
                let st0 = self.op_stack[ST0];
15,298✔
279
                hvs[0] = st0.inverse_or_zero();
15,298✔
280
                let next_opcode = self.next_instruction_or_argument().value();
15,298✔
281
                let decomposition = Self::decompose_opcode_for_instruction_skiz(next_opcode);
15,298✔
282
                hvs[1..6].copy_from_slice(&decomposition);
15,298✔
283
            }
15,298✔
284
            RecurseOrReturn => hvs[0] = (self.op_stack[ST6] - self.op_stack[ST5]).inverse_or_zero(),
401✔
285
            SpongeAbsorbMem => {
21✔
286
                hvs[0] = ram_read(self.op_stack[ST0] + bfe!(4));
21✔
287
                hvs[1] = ram_read(self.op_stack[ST0] + bfe!(5));
21✔
288
                hvs[2] = ram_read(self.op_stack[ST0] + bfe!(6));
21✔
289
                hvs[3] = ram_read(self.op_stack[ST0] + bfe!(7));
21✔
290
                hvs[4] = ram_read(self.op_stack[ST0] + bfe!(8));
21✔
291
                hvs[5] = ram_read(self.op_stack[ST0] + bfe!(9));
21✔
292
            }
21✔
293
            MerkleStep => {
6✔
294
                let divined_digest = self.secret_digests.front().copied().unwrap_or_default();
6✔
295
                let node_index = self.op_stack[ST5].value();
6✔
296
                hvs[..5].copy_from_slice(&divined_digest.values());
6✔
297
                hvs[5] = bfe!(node_index % 2);
6✔
298
            }
6✔
299
            MerkleStepMem => {
356✔
300
                let node_index = self.op_stack[ST5].value();
356✔
301
                let ram_pointer = self.op_stack[ST7];
356✔
302
                hvs[0] = ram_read(ram_pointer);
356✔
303
                hvs[1] = ram_read(ram_pointer + bfe!(1));
356✔
304
                hvs[2] = ram_read(ram_pointer + bfe!(2));
356✔
305
                hvs[3] = ram_read(ram_pointer + bfe!(3));
356✔
306
                hvs[4] = ram_read(ram_pointer + bfe!(4));
356✔
307
                hvs[5] = bfe!(node_index % 2);
356✔
308
            }
356✔
309
            Split => {
310
                let top_of_stack = self.op_stack[ST0].value();
31✔
311
                let lo = bfe!(top_of_stack & 0xffff_ffff);
31✔
312
                let hi = bfe!(top_of_stack >> 32);
31✔
313
                if !lo.is_zero() {
31✔
314
                    let max_val_of_hi = bfe!(2_u64.pow(32) - 1);
24✔
315
                    hvs[0] = (hi - max_val_of_hi).inverse_or_zero();
24✔
316
                }
24✔
317
            }
318
            Eq => hvs[0] = (self.op_stack[ST1] - self.op_stack[ST0]).inverse_or_zero(),
1,832✔
319
            XxDotStep => {
8✔
320
                hvs[0] = ram_read(self.op_stack[ST0]);
8✔
321
                hvs[1] = ram_read(self.op_stack[ST0] + bfe!(1));
8✔
322
                hvs[2] = ram_read(self.op_stack[ST0] + bfe!(2));
8✔
323
                hvs[3] = ram_read(self.op_stack[ST1]);
8✔
324
                hvs[4] = ram_read(self.op_stack[ST1] + bfe!(1));
8✔
325
                hvs[5] = ram_read(self.op_stack[ST1] + bfe!(2));
8✔
326
            }
8✔
327
            XbDotStep => {
6✔
328
                hvs[0] = ram_read(self.op_stack[ST0]);
6✔
329
                hvs[1] = ram_read(self.op_stack[ST1]);
6✔
330
                hvs[2] = ram_read(self.op_stack[ST1] + bfe!(1));
6✔
331
                hvs[3] = ram_read(self.op_stack[ST1] + bfe!(2));
6✔
332
            }
6✔
333
            _ => (),
129,304✔
334
        }
335

336
        hvs
227,331✔
337
    }
227,331✔
338

339
    fn decompose_opcode_for_instruction_skiz(opcode: u64) -> [BFieldElement; 5] {
15,298✔
340
        bfe_array![
15,298✔
341
            opcode % 2,
15,298✔
342
            (opcode >> 1) % 4,
15,298✔
343
            (opcode >> 3) % 4,
15,298✔
344
            (opcode >> 5) % 4,
15,298✔
345
            opcode >> 7,
15,298✔
346
        ]
15,298✔
347
    }
15,298✔
348

349
    /// Perform the state transition as a mutable operation on `self`.
350
    pub fn step(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
33,103,937✔
351
        if self.halting {
33,103,937✔
352
            return Err(InstructionError::MachineHalted);
×
353
        }
33,103,937✔
354

355
        let current_instruction = self.current_instruction()?;
33,103,937✔
356
        let op_stack_delta = current_instruction.op_stack_size_influence();
33,103,936✔
357
        if self.op_stack.would_be_too_shallow(op_stack_delta) {
33,103,936✔
358
            return Err(InstructionError::OpStackError(OpStackError::TooShallow));
3✔
359
        }
33,103,933✔
360

33,103,933✔
361
        self.start_recording_op_stack_calls();
33,103,933✔
362
        let mut co_processor_calls = match current_instruction {
33,103,933✔
363
            Pop(n) => self.pop(n)?,
6,986,450✔
364
            Push(field_element) => self.push(field_element),
10,128,278✔
365
            Divine(n) => self.divine(n)?,
307✔
366
            Dup(stack_element) => self.dup(stack_element),
372,096✔
367
            Swap(stack_element) => self.swap(stack_element),
199,662✔
368
            Halt => self.halt(),
4,600✔
369
            Nop => self.nop(),
57✔
370
            Skiz => self.skiz()?,
354,632✔
371
            Call(address) => self.call(address),
95,357✔
372
            Return => self.return_from_call()?,
94,488✔
373
            Recurse => self.recurse()?,
323,253✔
374
            RecurseOrReturn => self.recurse_or_return()?,
3,706✔
375
            Assert => self.assert()?,
34,272✔
376
            ReadMem(n) => self.read_mem(n)?,
6,208,011✔
377
            WriteMem(n) => self.write_mem(n)?,
777,675✔
378
            Hash => self.hash()?,
2,772✔
379
            SpongeInit => self.sponge_init(),
50,178✔
380
            SpongeAbsorb => self.sponge_absorb()?,
6,835✔
381
            SpongeAbsorbMem => self.sponge_absorb_mem()?,
2,563✔
382
            SpongeSqueeze => self.sponge_squeeze()?,
7,018✔
383
            AssertVector => self.assert_vector()?,
31,541✔
384
            Add => self.add()?,
62,742✔
385
            AddI(field_element) => self.addi(field_element),
973,828✔
386
            Mul => self.mul()?,
4,550✔
387
            Invert => self.invert()?,
262✔
388
            Eq => self.eq()?,
344,317✔
389
            Split => self.split()?,
294✔
390
            Lt => self.lt()?,
51✔
391
            And => self.and()?,
26✔
392
            Xor => self.xor()?,
25✔
393
            Log2Floor => self.log_2_floor()?,
304✔
394
            Pow => self.pow()?,
339✔
395
            DivMod => self.div_mod()?,
26✔
396
            PopCount => self.pop_count()?,
7✔
397
            XxAdd => self.xx_add()?,
2,806,534✔
398
            XxMul => self.xx_mul()?,
2,869,510✔
399
            XInvert => self.x_invert()?,
264✔
400
            XbMul => self.xb_mul()?,
262✔
401
            WriteIo(n) => self.write_io(n)?,
1,963✔
402
            ReadIo(n) => self.read_io(n)?,
67,621✔
403
            MerkleStep => self.merkle_step_non_determinism()?,
277,578✔
404
            MerkleStepMem => self.merkle_step_mem()?,
3,114✔
405
            XxDotStep => self.xx_dot_step()?,
3,252✔
406
            XbDotStep => self.xb_dot_step()?,
3,313✔
407
        };
408
        let op_stack_calls = self.stop_recording_op_stack_calls();
33,103,128✔
409
        co_processor_calls.extend(op_stack_calls);
33,103,128✔
410

33,103,128✔
411
        self.cycle_count += 1;
33,103,128✔
412

33,103,128✔
413
        Ok(co_processor_calls)
33,103,128✔
414
    }
33,103,937✔
415

416
    fn start_recording_op_stack_calls(&mut self) {
33,103,933✔
417
        self.op_stack.start_recording_underflow_io_sequence();
33,103,933✔
418
    }
33,103,933✔
419

420
    fn stop_recording_op_stack_calls(&mut self) -> Vec<CoProcessorCall> {
33,103,128✔
421
        let sequence = self.op_stack.stop_recording_underflow_io_sequence();
33,103,128✔
422
        self.underflow_io_sequence_to_co_processor_calls(sequence)
33,103,128✔
423
    }
33,103,128✔
424

425
    fn underflow_io_sequence_to_co_processor_calls(
33,103,128✔
426
        &self,
33,103,128✔
427
        underflow_io_sequence: Vec<UnderflowIO>,
33,103,128✔
428
    ) -> Vec<CoProcessorCall> {
33,103,128✔
429
        let op_stack_table_entries = OpStackTableEntry::from_underflow_io_sequence(
33,103,128✔
430
            self.cycle_count,
33,103,128✔
431
            self.op_stack.pointer(),
33,103,128✔
432
            underflow_io_sequence,
33,103,128✔
433
        );
33,103,128✔
434
        op_stack_table_entries
33,103,128✔
435
            .into_iter()
33,103,128✔
436
            .map(OpStackCall)
33,103,128✔
437
            .collect()
33,103,128✔
438
    }
33,103,128✔
439

440
    fn start_recording_ram_calls(&mut self) {
6,997,926✔
441
        self.ram_calls.clear();
6,997,926✔
442
    }
6,997,926✔
443

444
    fn stop_recording_ram_calls(&mut self) -> Vec<CoProcessorCall> {
6,997,926✔
445
        self.ram_calls.drain(..).map(RamCall).collect()
6,997,926✔
446
    }
6,997,926✔
447

448
    fn pop(&mut self, n: NumberOfWords) -> InstructionResult<Vec<CoProcessorCall>> {
6,986,450✔
449
        for _ in 0..n.num_words() {
6,986,450✔
450
            self.op_stack.pop()?;
6,990,437✔
451
        }
452

453
        self.instruction_pointer += 2;
6,986,450✔
454
        Ok(vec![])
6,986,450✔
455
    }
6,986,450✔
456

457
    fn push(&mut self, element: BFieldElement) -> Vec<CoProcessorCall> {
10,128,278✔
458
        self.op_stack.push(element);
10,128,278✔
459

10,128,278✔
460
        self.instruction_pointer += 2;
10,128,278✔
461
        vec![]
10,128,278✔
462
    }
10,128,278✔
463

464
    fn divine(&mut self, n: NumberOfWords) -> InstructionResult<Vec<CoProcessorCall>> {
307✔
465
        let input_len = self.secret_individual_tokens.len();
307✔
466
        if input_len < n.num_words() {
307✔
467
            return Err(InstructionError::EmptySecretInput(input_len));
1✔
468
        }
306✔
469
        for _ in 0..n.num_words() {
1,458✔
470
            let element = self.secret_individual_tokens.pop_front().unwrap();
1,458✔
471
            self.op_stack.push(element);
1,458✔
472
        }
1,458✔
473

474
        self.instruction_pointer += 2;
306✔
475
        Ok(vec![])
306✔
476
    }
307✔
477

478
    fn dup(&mut self, stack_register: OpStackElement) -> Vec<CoProcessorCall> {
372,096✔
479
        let element = self.op_stack[stack_register];
372,096✔
480
        self.op_stack.push(element);
372,096✔
481

372,096✔
482
        self.instruction_pointer += 2;
372,096✔
483
        vec![]
372,096✔
484
    }
372,096✔
485

486
    fn swap(&mut self, st: OpStackElement) -> Vec<CoProcessorCall> {
199,662✔
487
        (self.op_stack[ST0], self.op_stack[st]) = (self.op_stack[st], self.op_stack[ST0]);
199,662✔
488
        self.instruction_pointer += 2;
199,662✔
489
        vec![]
199,662✔
490
    }
199,662✔
491

492
    fn nop(&mut self) -> Vec<CoProcessorCall> {
57✔
493
        self.instruction_pointer += 1;
57✔
494
        vec![]
57✔
495
    }
57✔
496

497
    fn skiz(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
354,632✔
498
        let top_of_stack = self.op_stack.pop()?;
354,632✔
499
        self.instruction_pointer += match top_of_stack.is_zero() {
354,632✔
500
            true => 1 + self.next_instruction()?.size(),
309,651✔
501
            false => 1,
44,981✔
502
        };
503
        Ok(vec![])
354,632✔
504
    }
354,632✔
505

506
    fn call(&mut self, call_destination: BFieldElement) -> Vec<CoProcessorCall> {
95,357✔
507
        let size_of_instruction_call = 2;
95,357✔
508
        let call_origin = (self.instruction_pointer as u32 + size_of_instruction_call).into();
95,357✔
509
        let jump_stack_entry = (call_origin, call_destination);
95,357✔
510
        self.jump_stack.push(jump_stack_entry);
95,357✔
511

95,357✔
512
        self.instruction_pointer = call_destination.value().try_into().unwrap();
95,357✔
513
        vec![]
95,357✔
514
    }
95,357✔
515

516
    fn return_from_call(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
94,488✔
517
        let (call_origin, _) = self.jump_stack_pop()?;
94,488✔
518
        self.instruction_pointer = call_origin.value().try_into().unwrap();
94,486✔
519
        Ok(vec![])
94,486✔
520
    }
94,488✔
521

522
    fn recurse(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
323,253✔
523
        let (_, call_destination) = self.jump_stack_peek()?;
323,253✔
524
        self.instruction_pointer = call_destination.value().try_into().unwrap();
323,252✔
525
        Ok(vec![])
323,252✔
526
    }
323,253✔
527

528
    fn recurse_or_return(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
3,706✔
529
        if self.jump_stack.is_empty() {
3,706✔
530
            return Err(InstructionError::JumpStackIsEmpty);
1✔
531
        }
3,705✔
532

533
        let new_ip = if self.op_stack[ST5] == self.op_stack[ST6] {
3,705✔
534
            let (call_origin, _) = self.jump_stack_pop()?;
853✔
535
            call_origin
853✔
536
        } else {
537
            let (_, call_destination) = self.jump_stack_peek()?;
2,852✔
538
            call_destination
2,852✔
539
        };
540

541
        self.instruction_pointer = new_ip.value().try_into().unwrap();
3,705✔
542

3,705✔
543
        Ok(vec![])
3,705✔
544
    }
3,706✔
545

546
    fn assert(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
34,272✔
547
        if !self.op_stack[ST0].is_one() {
34,272✔
548
            return Err(InstructionError::AssertionFailed);
261✔
549
        }
34,011✔
550
        let _ = self.op_stack.pop()?;
34,011✔
551

552
        self.instruction_pointer += 1;
34,011✔
553
        Ok(vec![])
34,011✔
554
    }
34,272✔
555

556
    fn halt(&mut self) -> Vec<CoProcessorCall> {
4,600✔
557
        self.halting = true;
4,600✔
558
        self.instruction_pointer += 1;
4,600✔
559
        vec![]
4,600✔
560
    }
4,600✔
561

562
    fn read_mem(&mut self, n: NumberOfWords) -> InstructionResult<Vec<CoProcessorCall>> {
6,208,011✔
563
        self.start_recording_ram_calls();
6,208,011✔
564
        let mut ram_pointer = self.op_stack.pop()?;
6,208,011✔
565
        for _ in 0..n.num_words() {
16,611,454✔
566
            let ram_value = self.ram_read(ram_pointer);
16,611,454✔
567
            self.op_stack.push(ram_value);
16,611,454✔
568
            ram_pointer.decrement();
16,611,454✔
569
        }
16,611,454✔
570
        self.op_stack.push(ram_pointer);
6,208,011✔
571
        let ram_calls = self.stop_recording_ram_calls();
6,208,011✔
572

6,208,011✔
573
        self.instruction_pointer += 2;
6,208,011✔
574
        Ok(ram_calls)
6,208,011✔
575
    }
6,208,011✔
576

577
    fn write_mem(&mut self, n: NumberOfWords) -> InstructionResult<Vec<CoProcessorCall>> {
777,675✔
578
        self.start_recording_ram_calls();
777,675✔
579
        let mut ram_pointer = self.op_stack.pop()?;
777,675✔
580
        for _ in 0..n.num_words() {
777,675✔
581
            let ram_value = self.op_stack.pop()?;
2,269,469✔
582
            self.ram_write(ram_pointer, ram_value);
2,269,469✔
583
            ram_pointer.increment();
2,269,469✔
584
        }
585
        self.op_stack.push(ram_pointer);
777,675✔
586
        let ram_calls = self.stop_recording_ram_calls();
777,675✔
587

777,675✔
588
        self.instruction_pointer += 2;
777,675✔
589
        Ok(ram_calls)
777,675✔
590
    }
777,675✔
591

592
    fn ram_read(&mut self, ram_pointer: BFieldElement) -> BFieldElement {
16,685,403✔
593
        let ram_value = self
16,685,403✔
594
            .ram
16,685,403✔
595
            .get(&ram_pointer)
16,685,403✔
596
            .copied()
16,685,403✔
597
            .unwrap_or(BFieldElement::ZERO);
16,685,403✔
598

16,685,403✔
599
        let ram_table_call = RamTableCall {
16,685,403✔
600
            clk: self.cycle_count,
16,685,403✔
601
            ram_pointer,
16,685,403✔
602
            ram_value,
16,685,403✔
603
            is_write: false,
16,685,403✔
604
        };
16,685,403✔
605
        self.ram_calls.push(ram_table_call);
16,685,403✔
606

16,685,403✔
607
        ram_value
16,685,403✔
608
    }
16,685,403✔
609

610
    fn ram_write(&mut self, ram_pointer: BFieldElement, ram_value: BFieldElement) {
2,269,469✔
611
        let ram_table_call = RamTableCall {
2,269,469✔
612
            clk: self.cycle_count,
2,269,469✔
613
            ram_pointer,
2,269,469✔
614
            ram_value,
2,269,469✔
615
            is_write: true,
2,269,469✔
616
        };
2,269,469✔
617
        self.ram_calls.push(ram_table_call);
2,269,469✔
618

2,269,469✔
619
        self.ram.insert(ram_pointer, ram_value);
2,269,469✔
620
    }
2,269,469✔
621

622
    fn hash(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
2,772✔
623
        let to_hash = self.op_stack.pop_multiple::<{ tip5::RATE }>()?;
2,772✔
624

625
        let mut hash_input = Tip5::new(Domain::FixedLength);
2,772✔
626
        hash_input.state[..tip5::RATE].copy_from_slice(&to_hash);
2,772✔
627
        let tip5_trace = hash_input.trace();
2,772✔
628
        let hash_output = &tip5_trace[tip5_trace.len() - 1][0..Digest::LEN];
2,772✔
629

630
        for i in (0..Digest::LEN).rev() {
13,860✔
631
            self.op_stack.push(hash_output[i]);
13,860✔
632
        }
13,860✔
633

634
        let co_processor_calls = vec![Tip5Trace(Hash, Box::new(tip5_trace))];
2,772✔
635

2,772✔
636
        self.instruction_pointer += 1;
2,772✔
637
        Ok(co_processor_calls)
2,772✔
638
    }
2,772✔
639

640
    fn sponge_init(&mut self) -> Vec<CoProcessorCall> {
50,178✔
641
        self.sponge = Some(Tip5::init());
50,178✔
642
        self.instruction_pointer += 1;
50,178✔
643
        vec![SpongeStateReset]
50,178✔
644
    }
50,178✔
645

646
    fn sponge_absorb(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
6,835✔
647
        let Some(ref mut sponge) = self.sponge else {
6,835✔
648
            return Err(InstructionError::SpongeNotInitialized);
1✔
649
        };
650
        let to_absorb = self.op_stack.pop_multiple::<{ tip5::RATE }>()?;
6,834✔
651
        sponge.state[..tip5::RATE].copy_from_slice(&to_absorb);
6,834✔
652
        let tip5_trace = sponge.trace();
6,834✔
653

6,834✔
654
        let co_processor_calls = vec![Tip5Trace(SpongeAbsorb, Box::new(tip5_trace))];
6,834✔
655

6,834✔
656
        self.instruction_pointer += 1;
6,834✔
657
        Ok(co_processor_calls)
6,834✔
658
    }
6,835✔
659

660
    fn sponge_absorb_mem(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
2,563✔
661
        let Some(mut sponge) = self.sponge.take() else {
2,563✔
662
            return Err(InstructionError::SpongeNotInitialized);
1✔
663
        };
664

665
        self.start_recording_ram_calls();
2,562✔
666
        let mut mem_pointer = self.op_stack.pop()?;
2,562✔
667
        for i in 0..tip5::RATE {
28,182✔
668
            let element = self.ram_read(mem_pointer);
25,620✔
669
            mem_pointer.increment();
25,620✔
670
            sponge.state[i] = element;
25,620✔
671

25,620✔
672
            // there are not enough helper variables – overwrite part of the stack :(
25,620✔
673
            if i < tip5::RATE - NUM_HELPER_VARIABLE_REGISTERS {
25,620✔
674
                self.op_stack[i] = element;
10,248✔
675
            }
15,372✔
676
        }
677
        self.op_stack.push(mem_pointer);
2,562✔
678

2,562✔
679
        let tip5_trace = sponge.trace();
2,562✔
680
        self.sponge = Some(sponge);
2,562✔
681

2,562✔
682
        let mut co_processor_calls = self.stop_recording_ram_calls();
2,562✔
683
        co_processor_calls.push(Tip5Trace(SpongeAbsorb, Box::new(tip5_trace)));
2,562✔
684

2,562✔
685
        self.instruction_pointer += 1;
2,562✔
686
        Ok(co_processor_calls)
2,562✔
687
    }
2,563✔
688

689
    fn sponge_squeeze(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
7,018✔
690
        let Some(ref mut sponge) = self.sponge else {
7,018✔
691
            return Err(InstructionError::SpongeNotInitialized);
1✔
692
        };
693
        for i in (0..tip5::RATE).rev() {
70,170✔
694
            self.op_stack.push(sponge.state[i]);
70,170✔
695
        }
70,170✔
696
        let tip5_trace = sponge.trace();
7,017✔
697

7,017✔
698
        let co_processor_calls = vec![Tip5Trace(SpongeSqueeze, Box::new(tip5_trace))];
7,017✔
699

7,017✔
700
        self.instruction_pointer += 1;
7,017✔
701
        Ok(co_processor_calls)
7,017✔
702
    }
7,018✔
703

704
    fn assert_vector(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
31,541✔
705
        for i in 0..Digest::LEN {
188,491✔
706
            if self.op_stack[i] != self.op_stack[i + Digest::LEN] {
157,208✔
707
                return Err(InstructionError::VectorAssertionFailed(i));
258✔
708
            }
156,950✔
709
        }
710
        self.op_stack.pop_multiple::<{ Digest::LEN }>()?;
31,283✔
711
        self.instruction_pointer += 1;
31,283✔
712
        Ok(vec![])
31,283✔
713
    }
31,541✔
714

715
    fn add(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
62,742✔
716
        let lhs = self.op_stack.pop()?;
62,742✔
717
        let rhs = self.op_stack.pop()?;
62,742✔
718
        self.op_stack.push(lhs + rhs);
62,742✔
719

62,742✔
720
        self.instruction_pointer += 1;
62,742✔
721
        Ok(vec![])
62,742✔
722
    }
62,742✔
723

724
    fn addi(&mut self, i: BFieldElement) -> Vec<CoProcessorCall> {
973,828✔
725
        self.op_stack[ST0] += i;
973,828✔
726
        self.instruction_pointer += 2;
973,828✔
727
        vec![]
973,828✔
728
    }
973,828✔
729

730
    fn mul(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
4,550✔
731
        let lhs = self.op_stack.pop()?;
4,550✔
732
        let rhs = self.op_stack.pop()?;
4,550✔
733
        self.op_stack.push(lhs * rhs);
4,550✔
734

4,550✔
735
        self.instruction_pointer += 1;
4,550✔
736
        Ok(vec![])
4,550✔
737
    }
4,550✔
738

739
    fn invert(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
262✔
740
        let top_of_stack = self.op_stack[ST0];
262✔
741
        if top_of_stack.is_zero() {
262✔
742
            return Err(InstructionError::InverseOfZero);
2✔
743
        }
260✔
744
        let _ = self.op_stack.pop()?;
260✔
745
        self.op_stack.push(top_of_stack.inverse());
260✔
746
        self.instruction_pointer += 1;
260✔
747
        Ok(vec![])
260✔
748
    }
262✔
749

750
    fn eq(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
344,317✔
751
        let lhs = self.op_stack.pop()?;
344,317✔
752
        let rhs = self.op_stack.pop()?;
344,317✔
753
        let eq: u32 = (lhs == rhs).into();
344,317✔
754
        self.op_stack.push(eq.into());
344,317✔
755

344,317✔
756
        self.instruction_pointer += 1;
344,317✔
757
        Ok(vec![])
344,317✔
758
    }
344,317✔
759

760
    fn split(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
294✔
761
        let top_of_stack = self.op_stack.pop()?;
294✔
762
        let lo = bfe!(top_of_stack.value() & 0xffff_ffff);
294✔
763
        let hi = bfe!(top_of_stack.value() >> 32);
294✔
764
        self.op_stack.push(hi);
294✔
765
        self.op_stack.push(lo);
294✔
766

294✔
767
        let u32_table_entry = U32TableEntry::new(Split, lo, hi);
294✔
768
        let co_processor_calls = vec![U32Call(u32_table_entry)];
294✔
769

294✔
770
        self.instruction_pointer += 1;
294✔
771
        Ok(co_processor_calls)
294✔
772
    }
294✔
773

774
    fn lt(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
51✔
775
        self.op_stack.is_u32(ST0)?;
51✔
776
        self.op_stack.is_u32(ST1)?;
50✔
777
        let lhs = self.op_stack.pop_u32()?;
50✔
778
        let rhs = self.op_stack.pop_u32()?;
50✔
779
        let lt: u32 = (lhs < rhs).into();
50✔
780
        self.op_stack.push(lt.into());
50✔
781

50✔
782
        let u32_table_entry = U32TableEntry::new(Lt, lhs, rhs);
50✔
783
        let co_processor_calls = vec![U32Call(u32_table_entry)];
50✔
784

50✔
785
        self.instruction_pointer += 1;
50✔
786
        Ok(co_processor_calls)
50✔
787
    }
51✔
788

789
    fn and(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
26✔
790
        self.op_stack.is_u32(ST0)?;
26✔
791
        self.op_stack.is_u32(ST1)?;
25✔
792
        let lhs = self.op_stack.pop_u32()?;
24✔
793
        let rhs = self.op_stack.pop_u32()?;
24✔
794
        let and = lhs & rhs;
24✔
795
        self.op_stack.push(and.into());
24✔
796

24✔
797
        let u32_table_entry = U32TableEntry::new(And, lhs, rhs);
24✔
798
        let co_processor_calls = vec![U32Call(u32_table_entry)];
24✔
799

24✔
800
        self.instruction_pointer += 1;
24✔
801
        Ok(co_processor_calls)
24✔
802
    }
26✔
803

804
    fn xor(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
25✔
805
        self.op_stack.is_u32(ST0)?;
25✔
806
        self.op_stack.is_u32(ST1)?;
24✔
807
        let lhs = self.op_stack.pop_u32()?;
24✔
808
        let rhs = self.op_stack.pop_u32()?;
24✔
809
        let xor = lhs ^ rhs;
24✔
810
        self.op_stack.push(xor.into());
24✔
811

24✔
812
        // Triton VM uses the following equality to compute the results of both the `and`
24✔
813
        // and `xor` instruction using the u32 coprocessor's `and` capability:
24✔
814
        // a ^ b = a + b - 2 · (a & b)
24✔
815
        let u32_table_entry = U32TableEntry::new(And, lhs, rhs);
24✔
816
        let co_processor_calls = vec![U32Call(u32_table_entry)];
24✔
817

24✔
818
        self.instruction_pointer += 1;
24✔
819
        Ok(co_processor_calls)
24✔
820
    }
25✔
821

822
    fn log_2_floor(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
304✔
823
        self.op_stack.is_u32(ST0)?;
304✔
824
        let top_of_stack = self.op_stack[ST0];
47✔
825
        if top_of_stack.is_zero() {
47✔
826
            return Err(InstructionError::LogarithmOfZero);
3✔
827
        }
44✔
828
        let top_of_stack = self.op_stack.pop_u32()?;
44✔
829
        let log_2_floor = top_of_stack.ilog2();
44✔
830
        self.op_stack.push(log_2_floor.into());
44✔
831

44✔
832
        let u32_table_entry = U32TableEntry::new(Log2Floor, top_of_stack, 0);
44✔
833
        let co_processor_calls = vec![U32Call(u32_table_entry)];
44✔
834

44✔
835
        self.instruction_pointer += 1;
44✔
836
        Ok(co_processor_calls)
44✔
837
    }
304✔
838

839
    fn pow(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
339✔
840
        self.op_stack.is_u32(ST1)?;
339✔
841
        let base = self.op_stack.pop()?;
338✔
842
        let exponent = self.op_stack.pop_u32()?;
338✔
843
        let base_pow_exponent = base.mod_pow(exponent.into());
338✔
844
        self.op_stack.push(base_pow_exponent);
338✔
845

338✔
846
        let u32_table_entry = U32TableEntry::new(Pow, base, exponent);
338✔
847
        let co_processor_calls = vec![U32Call(u32_table_entry)];
338✔
848

338✔
849
        self.instruction_pointer += 1;
338✔
850
        Ok(co_processor_calls)
338✔
851
    }
339✔
852

853
    fn div_mod(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
26✔
854
        self.op_stack.is_u32(ST0)?;
26✔
855
        self.op_stack.is_u32(ST1)?;
26✔
856
        let denominator = self.op_stack[ST1];
25✔
857
        if denominator.is_zero() {
25✔
858
            return Err(InstructionError::DivisionByZero);
3✔
859
        }
22✔
860

861
        let numerator = self.op_stack.pop_u32()?;
22✔
862
        let denominator = self.op_stack.pop_u32()?;
22✔
863
        let quotient = numerator / denominator;
22✔
864
        let remainder = numerator % denominator;
22✔
865

22✔
866
        self.op_stack.push(quotient.into());
22✔
867
        self.op_stack.push(remainder.into());
22✔
868

22✔
869
        let remainder_is_less_than_denominator = U32TableEntry::new(Lt, remainder, denominator);
22✔
870
        let numerator_and_quotient_range_check = U32TableEntry::new(Split, numerator, quotient);
22✔
871
        let co_processor_calls = vec![
22✔
872
            U32Call(remainder_is_less_than_denominator),
22✔
873
            U32Call(numerator_and_quotient_range_check),
22✔
874
        ];
22✔
875

22✔
876
        self.instruction_pointer += 1;
22✔
877
        Ok(co_processor_calls)
22✔
878
    }
26✔
879

880
    fn pop_count(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
7✔
881
        self.op_stack.is_u32(ST0)?;
7✔
882
        let top_of_stack = self.op_stack.pop_u32()?;
6✔
883
        let pop_count = top_of_stack.count_ones();
6✔
884
        self.op_stack.push(pop_count.into());
6✔
885

6✔
886
        let u32_table_entry = U32TableEntry::new(PopCount, top_of_stack, 0);
6✔
887
        let co_processor_calls = vec![U32Call(u32_table_entry)];
6✔
888

6✔
889
        self.instruction_pointer += 1;
6✔
890
        Ok(co_processor_calls)
6✔
891
    }
7✔
892

893
    fn xx_add(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
2,806,534✔
894
        let lhs = self.op_stack.pop_extension_field_element()?;
2,806,534✔
895
        let rhs = self.op_stack.pop_extension_field_element()?;
2,806,534✔
896
        self.op_stack.push_extension_field_element(lhs + rhs);
2,806,534✔
897
        self.instruction_pointer += 1;
2,806,534✔
898
        Ok(vec![])
2,806,534✔
899
    }
2,806,534✔
900

901
    fn xx_mul(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
2,869,510✔
902
        let lhs = self.op_stack.pop_extension_field_element()?;
2,869,510✔
903
        let rhs = self.op_stack.pop_extension_field_element()?;
2,869,510✔
904
        self.op_stack.push_extension_field_element(lhs * rhs);
2,869,510✔
905
        self.instruction_pointer += 1;
2,869,510✔
906
        Ok(vec![])
2,869,510✔
907
    }
2,869,510✔
908

909
    fn x_invert(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
264✔
910
        let top_of_stack = self.op_stack.peek_at_top_extension_field_element();
264✔
911
        if top_of_stack.is_zero() {
264✔
912
            return Err(InstructionError::InverseOfZero);
2✔
913
        }
262✔
914
        let inverse = top_of_stack.inverse();
262✔
915
        let _ = self.op_stack.pop_extension_field_element()?;
262✔
916
        self.op_stack.push_extension_field_element(inverse);
262✔
917
        self.instruction_pointer += 1;
262✔
918
        Ok(vec![])
262✔
919
    }
264✔
920

921
    fn xb_mul(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
262✔
922
        let lhs = self.op_stack.pop()?;
262✔
923
        let rhs = self.op_stack.pop_extension_field_element()?;
262✔
924
        self.op_stack.push_extension_field_element(lhs.lift() * rhs);
262✔
925

262✔
926
        self.instruction_pointer += 1;
262✔
927
        Ok(vec![])
262✔
928
    }
262✔
929

930
    fn write_io(&mut self, n: NumberOfWords) -> InstructionResult<Vec<CoProcessorCall>> {
1,963✔
931
        for _ in 0..n.num_words() {
1,963✔
932
            let top_of_stack = self.op_stack.pop()?;
5,209✔
933
            self.public_output.push(top_of_stack);
5,209✔
934
        }
935

936
        self.instruction_pointer += 2;
1,963✔
937
        Ok(vec![])
1,963✔
938
    }
1,963✔
939

940
    fn read_io(&mut self, n: NumberOfWords) -> InstructionResult<Vec<CoProcessorCall>> {
67,621✔
941
        let input_len = self.public_input.len();
67,621✔
942
        if input_len < n.num_words() {
67,621✔
943
            return Err(InstructionError::EmptyPublicInput(input_len));
1✔
944
        }
67,620✔
945
        for _ in 0..n.num_words() {
197,653✔
946
            let read_element = self.public_input.pop_front().unwrap();
197,653✔
947
            self.op_stack.push(read_element);
197,653✔
948
        }
197,653✔
949

950
        self.instruction_pointer += 2;
67,620✔
951
        Ok(vec![])
67,620✔
952
    }
67,621✔
953

954
    fn merkle_step_non_determinism(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
277,578✔
955
        self.op_stack.is_u32(ST5)?;
277,578✔
956
        let sibling_digest = self.pop_secret_digest()?;
277,577✔
957
        self.merkle_step(sibling_digest)
277,576✔
958
    }
277,578✔
959

960
    fn merkle_step_mem(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
3,114✔
961
        self.op_stack.is_u32(ST5)?;
3,114✔
962
        self.start_recording_ram_calls();
3,113✔
963
        let mut ram_pointer = self.op_stack[ST7];
3,113✔
964
        let Digest(mut sibling_digest) = Digest::default();
3,113✔
965
        for digest_element in &mut sibling_digest {
18,678✔
966
            *digest_element = self.ram_read(ram_pointer);
15,565✔
967
            ram_pointer.increment();
15,565✔
968
        }
15,565✔
969
        self.op_stack[ST7] = ram_pointer;
3,113✔
970

971
        let mut co_processor_calls = self.merkle_step(sibling_digest)?;
3,113✔
972
        co_processor_calls.extend(self.stop_recording_ram_calls());
3,113✔
973
        Ok(co_processor_calls)
3,113✔
974
    }
3,114✔
975

976
    fn merkle_step(
280,689✔
977
        &mut self,
280,689✔
978
        sibling_digest: [BFieldElement; Digest::LEN],
280,689✔
979
    ) -> InstructionResult<Vec<CoProcessorCall>> {
280,689✔
980
        let node_index = self.op_stack.get_u32(ST5)?;
280,689✔
981
        let parent_node_index = node_index / 2;
280,689✔
982

983
        let accumulator_digest = self.op_stack.pop_multiple::<{ Digest::LEN }>()?;
280,689✔
984
        let (left_sibling, right_sibling) = match node_index % 2 {
280,689✔
985
            0 => (accumulator_digest, sibling_digest),
140,339✔
986
            1 => (sibling_digest, accumulator_digest),
140,350✔
987
            _ => unreachable!(),
×
988
        };
989

990
        let mut tip5 = Tip5::new(Domain::FixedLength);
280,689✔
991
        tip5.state[..Digest::LEN].copy_from_slice(&left_sibling);
280,689✔
992
        tip5.state[Digest::LEN..2 * Digest::LEN].copy_from_slice(&right_sibling);
280,689✔
993
        let tip5_trace = tip5.trace();
280,689✔
994
        let accumulator_digest = &tip5_trace.last().unwrap()[0..Digest::LEN];
280,689✔
995

996
        for &digest_element in accumulator_digest.iter().rev() {
1,403,445✔
997
            self.op_stack.push(digest_element);
1,403,445✔
998
        }
1,403,445✔
999
        self.op_stack[ST5] = parent_node_index.into();
280,689✔
1000

280,689✔
1001
        self.instruction_pointer += 1;
280,689✔
1002

280,689✔
1003
        let hash_co_processor_call = Tip5Trace(Hash, Box::new(tip5_trace));
280,689✔
1004
        let indices_are_u32 = U32Call(U32TableEntry::new(Split, node_index, parent_node_index));
280,689✔
1005
        let co_processor_calls = vec![hash_co_processor_call, indices_are_u32];
280,689✔
1006
        Ok(co_processor_calls)
280,689✔
1007
    }
280,689✔
1008

1009
    fn xx_dot_step(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
3,252✔
1010
        self.start_recording_ram_calls();
3,252✔
1011
        let mut rhs_address = self.op_stack.pop()?;
3,252✔
1012
        let mut lhs_address = self.op_stack.pop()?;
3,252✔
1013
        let mut rhs = xfe!(0);
3,252✔
1014
        let mut lhs = xfe!(0);
3,252✔
1015
        for i in 0..EXTENSION_DEGREE {
13,008✔
1016
            rhs.coefficients[i] = self.ram_read(rhs_address);
9,756✔
1017
            rhs_address.increment();
9,756✔
1018
            lhs.coefficients[i] = self.ram_read(lhs_address);
9,756✔
1019
            lhs_address.increment();
9,756✔
1020
        }
9,756✔
1021
        let accumulator = self.op_stack.pop_extension_field_element()? + rhs * lhs;
3,252✔
1022
        self.op_stack.push_extension_field_element(accumulator);
3,252✔
1023
        self.op_stack.push(lhs_address);
3,252✔
1024
        self.op_stack.push(rhs_address);
3,252✔
1025
        self.instruction_pointer += 1;
3,252✔
1026
        let ram_calls = self.stop_recording_ram_calls();
3,252✔
1027
        Ok(ram_calls)
3,252✔
1028
    }
3,252✔
1029

1030
    fn xb_dot_step(&mut self) -> InstructionResult<Vec<CoProcessorCall>> {
3,313✔
1031
        self.start_recording_ram_calls();
3,313✔
1032
        let mut rhs_address = self.op_stack.pop()?;
3,313✔
1033
        let mut lhs_address = self.op_stack.pop()?;
3,313✔
1034
        let rhs = self.ram_read(rhs_address);
3,313✔
1035
        rhs_address.increment();
3,313✔
1036
        let mut lhs = xfe!(0);
3,313✔
1037
        for i in 0..EXTENSION_DEGREE {
13,252✔
1038
            lhs.coefficients[i] = self.ram_read(lhs_address);
9,939✔
1039
            lhs_address.increment();
9,939✔
1040
        }
9,939✔
1041
        let accumulator = self.op_stack.pop_extension_field_element()? + rhs * lhs;
3,313✔
1042
        self.op_stack.push_extension_field_element(accumulator);
3,313✔
1043
        self.op_stack.push(lhs_address);
3,313✔
1044
        self.op_stack.push(rhs_address);
3,313✔
1045
        self.instruction_pointer += 1;
3,313✔
1046
        let ram_calls = self.stop_recording_ram_calls();
3,313✔
1047
        Ok(ram_calls)
3,313✔
1048
    }
3,313✔
1049

1050
    pub fn to_processor_row(&self) -> Array1<BFieldElement> {
227,331✔
1051
        use isa::instruction::InstructionBit;
1052
        use ProcessorMainColumn::*;
1053
        let mut processor_row = Array1::zeros(<ProcessorTable as AIR>::MainColumn::COUNT);
227,331✔
1054

227,331✔
1055
        let current_instruction = self.current_instruction().unwrap_or(Nop);
227,331✔
1056
        let helper_variables = self.derive_helper_variables();
227,331✔
1057

227,331✔
1058
        processor_row[CLK.main_index()] = u64::from(self.cycle_count).into();
227,331✔
1059
        processor_row[IP.main_index()] = (self.instruction_pointer as u32).into();
227,331✔
1060
        processor_row[CI.main_index()] = current_instruction.opcode_b();
227,331✔
1061
        processor_row[NIA.main_index()] = self.next_instruction_or_argument();
227,331✔
1062
        processor_row[IB0.main_index()] = current_instruction.ib(InstructionBit::IB0);
227,331✔
1063
        processor_row[IB1.main_index()] = current_instruction.ib(InstructionBit::IB1);
227,331✔
1064
        processor_row[IB2.main_index()] = current_instruction.ib(InstructionBit::IB2);
227,331✔
1065
        processor_row[IB3.main_index()] = current_instruction.ib(InstructionBit::IB3);
227,331✔
1066
        processor_row[IB4.main_index()] = current_instruction.ib(InstructionBit::IB4);
227,331✔
1067
        processor_row[IB5.main_index()] = current_instruction.ib(InstructionBit::IB5);
227,331✔
1068
        processor_row[IB6.main_index()] = current_instruction.ib(InstructionBit::IB6);
227,331✔
1069
        processor_row[JSP.main_index()] = self.jump_stack_pointer();
227,331✔
1070
        processor_row[JSO.main_index()] = self.jump_stack_origin();
227,331✔
1071
        processor_row[JSD.main_index()] = self.jump_stack_destination();
227,331✔
1072
        processor_row[ST0.main_index()] = self.op_stack[OpStackElement::ST0];
227,331✔
1073
        processor_row[ST1.main_index()] = self.op_stack[OpStackElement::ST1];
227,331✔
1074
        processor_row[ST2.main_index()] = self.op_stack[OpStackElement::ST2];
227,331✔
1075
        processor_row[ST3.main_index()] = self.op_stack[OpStackElement::ST3];
227,331✔
1076
        processor_row[ST4.main_index()] = self.op_stack[OpStackElement::ST4];
227,331✔
1077
        processor_row[ST5.main_index()] = self.op_stack[OpStackElement::ST5];
227,331✔
1078
        processor_row[ST6.main_index()] = self.op_stack[OpStackElement::ST6];
227,331✔
1079
        processor_row[ST7.main_index()] = self.op_stack[OpStackElement::ST7];
227,331✔
1080
        processor_row[ST8.main_index()] = self.op_stack[OpStackElement::ST8];
227,331✔
1081
        processor_row[ST9.main_index()] = self.op_stack[OpStackElement::ST9];
227,331✔
1082
        processor_row[ST10.main_index()] = self.op_stack[OpStackElement::ST10];
227,331✔
1083
        processor_row[ST11.main_index()] = self.op_stack[OpStackElement::ST11];
227,331✔
1084
        processor_row[ST12.main_index()] = self.op_stack[OpStackElement::ST12];
227,331✔
1085
        processor_row[ST13.main_index()] = self.op_stack[OpStackElement::ST13];
227,331✔
1086
        processor_row[ST14.main_index()] = self.op_stack[OpStackElement::ST14];
227,331✔
1087
        processor_row[ST15.main_index()] = self.op_stack[OpStackElement::ST15];
227,331✔
1088
        processor_row[OpStackPointer.main_index()] = self.op_stack.pointer();
227,331✔
1089
        processor_row[HV0.main_index()] = helper_variables[0];
227,331✔
1090
        processor_row[HV1.main_index()] = helper_variables[1];
227,331✔
1091
        processor_row[HV2.main_index()] = helper_variables[2];
227,331✔
1092
        processor_row[HV3.main_index()] = helper_variables[3];
227,331✔
1093
        processor_row[HV4.main_index()] = helper_variables[4];
227,331✔
1094
        processor_row[HV5.main_index()] = helper_variables[5];
227,331✔
1095

227,331✔
1096
        processor_row
227,331✔
1097
    }
227,331✔
1098

1099
    /// The “next instruction or argument” (NIA) is
1100
    /// - the argument of the current instruction if it has one, or
1101
    /// - the opcode of the next instruction otherwise.
1102
    ///
1103
    /// If the current instruction has no argument and there is no next instruction, the NIA is 1
1104
    /// to account for the hash-input padding separator of the program.
1105
    ///
1106
    /// If the instruction pointer is out of bounds, the returned NIA is 0.
1107
    fn next_instruction_or_argument(&self) -> BFieldElement {
242,629✔
1108
        let Ok(current_instruction) = self.current_instruction() else {
242,629✔
1109
            return bfe!(0);
×
1110
        };
1111
        if let Some(argument) = current_instruction.arg() {
242,629✔
1112
            return argument;
102,623✔
1113
        }
140,006✔
1114
        match self.next_instruction() {
140,006✔
1115
            Ok(next_instruction) => next_instruction.opcode_b(),
137,732✔
1116
            Err(_) => bfe!(1),
2,274✔
1117
        }
1118
    }
242,629✔
1119

1120
    fn jump_stack_pointer(&self) -> BFieldElement {
227,331✔
1121
        (self.jump_stack.len() as u64).into()
227,331✔
1122
    }
227,331✔
1123

1124
    fn jump_stack_origin(&self) -> BFieldElement {
227,331✔
1125
        let maybe_origin = self.jump_stack.last().map(|(o, _d)| *o);
227,331✔
1126
        maybe_origin.unwrap_or_else(BFieldElement::zero)
227,331✔
1127
    }
227,331✔
1128

1129
    fn jump_stack_destination(&self) -> BFieldElement {
227,331✔
1130
        let maybe_destination = self.jump_stack.last().map(|(_o, d)| *d);
227,331✔
1131
        maybe_destination.unwrap_or_else(BFieldElement::zero)
227,331✔
1132
    }
227,331✔
1133

1134
    pub fn current_instruction(&self) -> InstructionResult<Instruction> {
34,251,294✔
1135
        let maybe_current_instruction = self.program.get(self.instruction_pointer).copied();
34,251,294✔
1136
        maybe_current_instruction.ok_or(InstructionError::InstructionPointerOverflow)
34,251,294✔
1137
    }
34,251,294✔
1138

1139
    /// Return the next instruction on the tape, skipping arguments.
1140
    ///
1141
    /// Note that this is not necessarily the next instruction to execute, since the current
1142
    /// instruction could be a jump, but it is either program[ip + 1] or program[ip + 2],
1143
    /// depending on whether the current instruction takes an argument.
1144
    pub fn next_instruction(&self) -> InstructionResult<Instruction> {
449,657✔
1145
        let current_instruction = self.current_instruction()?;
449,657✔
1146
        let next_instruction_pointer = self.instruction_pointer + current_instruction.size();
449,657✔
1147
        let maybe_next_instruction = self.program.get(next_instruction_pointer).copied();
449,657✔
1148
        maybe_next_instruction.ok_or(InstructionError::InstructionPointerOverflow)
449,657✔
1149
    }
449,657✔
1150

1151
    fn jump_stack_pop(&mut self) -> InstructionResult<(BFieldElement, BFieldElement)> {
95,341✔
1152
        self.jump_stack
95,341✔
1153
            .pop()
95,341✔
1154
            .ok_or(InstructionError::JumpStackIsEmpty)
95,341✔
1155
    }
95,341✔
1156

1157
    fn jump_stack_peek(&mut self) -> InstructionResult<(BFieldElement, BFieldElement)> {
326,105✔
1158
        self.jump_stack
326,105✔
1159
            .last()
326,105✔
1160
            .copied()
326,105✔
1161
            .ok_or(InstructionError::JumpStackIsEmpty)
326,105✔
1162
    }
326,105✔
1163

1164
    fn pop_secret_digest(&mut self) -> InstructionResult<[BFieldElement; Digest::LEN]> {
277,577✔
1165
        let digest = self
277,577✔
1166
            .secret_digests
277,577✔
1167
            .pop_front()
277,577✔
1168
            .ok_or(InstructionError::EmptySecretDigestInput)?;
277,577✔
1169
        Ok(digest.values())
277,576✔
1170
    }
277,577✔
1171

1172
    /// Run Triton VM on this state to completion, or until an error occurs.
1173
    pub fn run(&mut self) -> InstructionResult<()> {
4,704✔
1174
        while !self.halting {
32,880,061✔
1175
            self.step()?;
32,876,141✔
1176
        }
1177
        Ok(())
3,920✔
1178
    }
4,704✔
1179
}
1180

1181
impl Display for VMState {
1182
    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
2✔
1183
        use ProcessorMainColumn as ProcCol;
1184

1185
        let Ok(instruction) = self.current_instruction() else {
2✔
1186
            return write!(f, "END-OF-FILE");
×
1187
        };
1188

1189
        let total_width = 103;
2✔
1190
        let tab_width = 54;
2✔
1191
        let clk_width = 17;
2✔
1192
        let register_width = 20;
2✔
1193
        let buffer_width = total_width - tab_width - clk_width - 7;
2✔
1194

2✔
1195
        let print_row = |f: &mut Formatter, s: String| writeln!(f, "│ {s: <total_width$} │");
26✔
1196
        let print_blank_row = |f: &mut Formatter| print_row(f, String::new());
4✔
1197

1198
        let row = self.to_processor_row();
2✔
1199

2✔
1200
        let register = |reg: ProcessorMainColumn| {
58✔
1201
            let reg_string = format!("{}", row[reg.main_index()]);
58✔
1202
            format!("{reg_string:>register_width$}")
58✔
1203
        };
58✔
1204
        let multi_register = |regs: [_; 4]| regs.map(register).join(" | ");
10✔
1205

1206
        writeln!(f)?;
2✔
1207
        writeln!(f, " ╭─{:─<tab_width$}─╮", "")?;
2✔
1208
        writeln!(f, " │ {: <tab_width$} │", format!("{instruction}"))?;
2✔
1209
        writeln!(
2✔
1210
            f,
2✔
1211
            "╭┴─{:─<tab_width$}─┴─{:─<buffer_width$}─┬─{:─>clk_width$}─╮",
2✔
1212
            "", "", ""
2✔
1213
        )?;
2✔
1214

1215
        let ip = register(ProcCol::IP);
2✔
1216
        let ci = register(ProcCol::CI);
2✔
1217
        let nia = register(ProcCol::NIA);
2✔
1218
        let jsp = register(ProcCol::JSP);
2✔
1219
        let jso = register(ProcCol::JSO);
2✔
1220
        let jsd = register(ProcCol::JSD);
2✔
1221
        let osp = register(ProcCol::OpStackPointer);
2✔
1222
        let clk = row[ProcCol::CLK.main_index()].to_string();
2✔
1223
        let clk = clk.trim_start_matches('0');
2✔
1224

2✔
1225
        let first_line = format!("ip:   {ip} ╷ ci:   {ci} ╷ nia: {nia} │ {clk: >clk_width$}");
2✔
1226
        print_row(f, first_line)?;
2✔
1227
        writeln!(
2✔
1228
            f,
2✔
1229
            "│ jsp:  {jsp} │ jso:  {jso} │ jsd: {jsd} ╰─{:─>clk_width$}─┤",
2✔
1230
            "",
2✔
1231
        )?;
2✔
1232
        print_row(f, format!("osp:  {osp} ╵"))?;
2✔
1233
        print_blank_row(f)?;
2✔
1234

1235
        let st_00_03 = multi_register([ProcCol::ST0, ProcCol::ST1, ProcCol::ST2, ProcCol::ST3]);
2✔
1236
        let st_04_07 = multi_register([ProcCol::ST4, ProcCol::ST5, ProcCol::ST6, ProcCol::ST7]);
2✔
1237
        let st_08_11 = multi_register([ProcCol::ST8, ProcCol::ST9, ProcCol::ST10, ProcCol::ST11]);
2✔
1238
        let st_12_15 = multi_register([ProcCol::ST12, ProcCol::ST13, ProcCol::ST14, ProcCol::ST15]);
2✔
1239

2✔
1240
        print_row(f, format!("st0-3:    [ {st_00_03} ]"))?;
2✔
1241
        print_row(f, format!("st4-7:    [ {st_04_07} ]"))?;
2✔
1242
        print_row(f, format!("st8-11:   [ {st_08_11} ]"))?;
2✔
1243
        print_row(f, format!("st12-15:  [ {st_12_15} ]"))?;
2✔
1244
        print_blank_row(f)?;
2✔
1245

1246
        let hv_00_03_line = format!(
2✔
1247
            "hv0-3:    [ {} ]",
2✔
1248
            multi_register([ProcCol::HV0, ProcCol::HV1, ProcCol::HV2, ProcCol::HV3])
2✔
1249
        );
2✔
1250
        let hv_04_05_line = format!(
2✔
1251
            "hv4-5:    [ {} | {} ]",
2✔
1252
            register(ProcCol::HV4),
2✔
1253
            register(ProcCol::HV5),
2✔
1254
        );
2✔
1255
        print_row(f, hv_00_03_line)?;
2✔
1256
        print_row(f, hv_04_05_line)?;
2✔
1257

1258
        let ib_registers = [
2✔
1259
            ProcCol::IB6,
2✔
1260
            ProcCol::IB5,
2✔
1261
            ProcCol::IB4,
2✔
1262
            ProcCol::IB3,
2✔
1263
            ProcCol::IB2,
2✔
1264
            ProcCol::IB1,
2✔
1265
            ProcCol::IB0,
2✔
1266
        ]
2✔
1267
        .map(|reg| row[reg.main_index()])
14✔
1268
        .map(|bfe| format!("{bfe:>2}"))
14✔
1269
        .join(" | ");
2✔
1270
        print_row(f, format!("ib6-0:    [ {ib_registers} ]",))?;
2✔
1271

1272
        let Some(ref sponge) = self.sponge else {
2✔
1273
            return writeln!(f, "╰─{:─<total_width$}─╯", "");
1✔
1274
        };
1275

1276
        let sponge_state_register = |i: usize| sponge.state[i].value();
16✔
1277
        let sponge_state_slice = |idxs: Range<usize>| {
4✔
1278
            idxs.map(sponge_state_register)
4✔
1279
                .map(|ss| format!("{ss:>register_width$}"))
16✔
1280
                .join(" | ")
4✔
1281
        };
4✔
1282

1283
        let sponge_state_00_03 = sponge_state_slice(0..4);
1✔
1284
        let sponge_state_04_07 = sponge_state_slice(4..8);
1✔
1285
        let sponge_state_08_11 = sponge_state_slice(8..12);
1✔
1286
        let sponge_state_12_15 = sponge_state_slice(12..16);
1✔
1287

1✔
1288
        writeln!(f, "├─{:─<total_width$}─┤", "")?;
1✔
1289
        print_row(f, format!("sp0-3:    [ {sponge_state_00_03} ]"))?;
1✔
1290
        print_row(f, format!("sp4-7:    [ {sponge_state_04_07} ]"))?;
1✔
1291
        print_row(f, format!("sp8-11:   [ {sponge_state_08_11} ]"))?;
1✔
1292
        print_row(f, format!("sp12-15:  [ {sponge_state_12_15} ]"))?;
1✔
1293
        writeln!(f, "╰─{:─<total_width$}─╯", "")
1✔
1294
    }
2✔
1295
}
1296

1297
#[derive(Debug, Default, Clone, Eq, PartialEq, BFieldCodec, Arbitrary)]
×
1298
pub struct PublicInput {
1299
    pub individual_tokens: Vec<BFieldElement>,
1300
}
1301

1302
impl From<Vec<BFieldElement>> for PublicInput {
1303
    fn from(individual_tokens: Vec<BFieldElement>) -> Self {
1,352✔
1304
        Self::new(individual_tokens)
1,352✔
1305
    }
1,352✔
1306
}
1307

1308
impl From<&Vec<BFieldElement>> for PublicInput {
1309
    fn from(tokens: &Vec<BFieldElement>) -> Self {
258✔
1310
        Self::new(tokens.to_owned())
258✔
1311
    }
258✔
1312
}
1313

1314
impl<const N: usize> From<[BFieldElement; N]> for PublicInput {
1315
    fn from(tokens: [BFieldElement; N]) -> Self {
2,454✔
1316
        Self::new(tokens.to_vec())
2,454✔
1317
    }
2,454✔
1318
}
1319

1320
impl From<&[BFieldElement]> for PublicInput {
1321
    fn from(tokens: &[BFieldElement]) -> Self {
513✔
1322
        Self::new(tokens.to_vec())
513✔
1323
    }
513✔
1324
}
1325

1326
impl PublicInput {
1327
    pub fn new(individual_tokens: Vec<BFieldElement>) -> Self {
5,092✔
1328
        Self { individual_tokens }
5,092✔
1329
    }
5,092✔
1330
}
1331

1332
/// All sources of non-determinism for a program. This includes elements that
1333
/// can be read using instruction `divine`, digests that can be read using
1334
/// instruction `merkle_step`, and an initial state of random-access memory.
1335
#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize, Arbitrary)]
×
1336
pub struct NonDeterminism {
1337
    pub individual_tokens: Vec<BFieldElement>,
1338
    pub digests: Vec<Digest>,
1339
    pub ram: HashMap<BFieldElement, BFieldElement>,
1340
}
1341

1342
impl From<Vec<BFieldElement>> for NonDeterminism {
1343
    fn from(tokens: Vec<BFieldElement>) -> Self {
296✔
1344
        Self::new(tokens)
296✔
1345
    }
296✔
1346
}
1347

1348
impl From<&Vec<BFieldElement>> for NonDeterminism {
1349
    fn from(tokens: &Vec<BFieldElement>) -> Self {
×
1350
        Self::new(tokens.to_owned())
×
1351
    }
×
1352
}
1353

1354
impl<const N: usize> From<[BFieldElement; N]> for NonDeterminism {
1355
    fn from(tokens: [BFieldElement; N]) -> Self {
2,133✔
1356
        Self::new(tokens.to_vec())
2,133✔
1357
    }
2,133✔
1358
}
1359

1360
impl From<&[BFieldElement]> for NonDeterminism {
1361
    fn from(tokens: &[BFieldElement]) -> Self {
512✔
1362
        Self::new(tokens.to_vec())
512✔
1363
    }
512✔
1364
}
1365

1366
impl NonDeterminism {
1367
    pub fn new<V: Into<Vec<BFieldElement>>>(individual_tokens: V) -> Self {
3,711✔
1368
        Self {
3,711✔
1369
            individual_tokens: individual_tokens.into(),
3,711✔
1370
            digests: vec![],
3,711✔
1371
            ram: HashMap::new(),
3,711✔
1372
        }
3,711✔
1373
    }
3,711✔
1374

1375
    #[must_use]
1376
    pub fn with_digests<V: Into<Vec<Digest>>>(mut self, digests: V) -> Self {
308✔
1377
        self.digests = digests.into();
308✔
1378
        self
308✔
1379
    }
308✔
1380

1381
    #[must_use]
1382
    pub fn with_ram<H: Into<HashMap<BFieldElement, BFieldElement>>>(mut self, ram: H) -> Self {
1,848✔
1383
        self.ram = ram.into();
1,848✔
1384
        self
1,848✔
1385
    }
1,848✔
1386
}
1387

1388
#[cfg(test)]
1389
pub(crate) mod tests {
1390
    use std::ops::BitAnd;
1391
    use std::ops::BitXor;
1392

1393
    use air::table::TableId;
1394
    use assert2::assert;
1395
    use assert2::let_assert;
1396
    use isa::instruction::AnInstruction;
1397
    use isa::instruction::LabelledInstruction;
1398
    use isa::instruction::ALL_INSTRUCTIONS;
1399
    use isa::program::Program;
1400
    use isa::triton_asm;
1401
    use isa::triton_instr;
1402
    use isa::triton_program;
1403
    use itertools::izip;
1404
    use proptest::collection::vec;
1405
    use proptest::prelude::*;
1406
    use proptest_arbitrary_interop::arb;
1407
    use rand::rngs::ThreadRng;
1408
    use rand::Rng;
1409
    use rand::RngCore;
1410
    use strum::EnumCount;
1411
    use strum::EnumIter;
1412
    use test_strategy::proptest;
1413
    use twenty_first::math::other::random_elements;
1414

1415
    use crate::example_programs::*;
1416
    use crate::shared_tests::prove_and_verify;
1417
    use crate::shared_tests::LeavedMerkleTreeTestData;
1418
    use crate::shared_tests::ProgramAndInput;
1419
    use crate::shared_tests::DEFAULT_LOG2_FRI_EXPANSION_FACTOR_FOR_TESTS;
1420

1421
    use super::*;
1422

1423
    #[test]
1424
    fn instructions_act_on_op_stack_as_indicated() {
1✔
1425
        for test_instruction in ALL_INSTRUCTIONS {
45✔
1426
            let (program, stack_size_before_test_instruction) =
44✔
1427
                construct_test_program_for_instruction(test_instruction);
44✔
1428
            let public_input = PublicInput::from(bfe_array![0]);
44✔
1429
            let mock_digests = [Digest::default()];
44✔
1430
            let non_determinism = NonDeterminism::from(bfe_array![0]).with_digests(mock_digests);
44✔
1431

44✔
1432
            let mut vm_state = VMState::new(&program, public_input, non_determinism);
44✔
1433
            let_assert!(Ok(()) = vm_state.run());
44✔
1434
            let stack_size_after_test_instruction = vm_state.op_stack.len();
44✔
1435

44✔
1436
            let stack_size_difference = (stack_size_after_test_instruction as i32)
44✔
1437
                - (stack_size_before_test_instruction as i32);
44✔
1438
            assert!(
44✔
1439
                test_instruction.op_stack_size_influence() == stack_size_difference,
44✔
1440
                "{test_instruction}"
×
1441
            );
1442
        }
1443
    }
1✔
1444

1445
    fn construct_test_program_for_instruction(
44✔
1446
        instruction: AnInstruction<BFieldElement>,
44✔
1447
    ) -> (Program, usize) {
44✔
1448
        if matches!(instruction, Call(_) | Return | Recurse | RecurseOrReturn) {
44✔
1449
            // need jump stack setup
1450
            let program = test_program_for_call_recurse_return().program;
4✔
1451
            let stack_size = NUM_OP_STACK_REGISTERS;
4✔
1452
            (program, stack_size)
4✔
1453
        } else {
1454
            let num_push_instructions = 10;
40✔
1455
            let push_instructions = triton_asm![push 1; num_push_instructions];
40✔
1456
            let program = triton_program!(sponge_init {&push_instructions} {instruction} nop halt);
40✔
1457

40✔
1458
            let stack_size_when_reaching_test_instruction =
40✔
1459
                NUM_OP_STACK_REGISTERS + num_push_instructions;
40✔
1460
            (program, stack_size_when_reaching_test_instruction)
40✔
1461
        }
1462
    }
44✔
1463

1464
    #[test]
1465
    fn profile_can_be_created_and_agrees_with_regular_vm_run() {
1✔
1466
        let program = CALCULATE_NEW_MMR_PEAKS_FROM_APPEND_WITH_SAFE_LISTS.clone();
1✔
1467
        let (profile_output, profile) = VM::profile(&program, [].into(), [].into()).unwrap();
1✔
1468
        let mut vm_state = VMState::new(&program, [].into(), [].into());
1✔
1469
        let_assert!(Ok(()) = vm_state.run());
1✔
1470
        assert!(profile_output == vm_state.public_output);
1✔
1471
        assert!(profile.total.processor == vm_state.cycle_count);
1✔
1472

1473
        let_assert!(Ok((aet, trace_output)) = VM::trace_execution(&program, [].into(), [].into()));
1✔
1474
        assert!(profile_output == trace_output);
1✔
1475
        let proc_height = u32::try_from(aet.height_of_table(TableId::Processor)).unwrap();
1✔
1476
        assert!(proc_height == profile.total.processor);
1✔
1477

1478
        let op_stack_height = u32::try_from(aet.height_of_table(TableId::OpStack)).unwrap();
1✔
1479
        assert!(op_stack_height == profile.total.op_stack);
1✔
1480

1481
        let ram_height = u32::try_from(aet.height_of_table(TableId::Ram)).unwrap();
1✔
1482
        assert!(ram_height == profile.total.ram);
1✔
1483

1484
        let hash_height = u32::try_from(aet.height_of_table(TableId::Hash)).unwrap();
1✔
1485
        assert!(hash_height == profile.total.hash);
1✔
1486

1487
        let u32_height = u32::try_from(aet.height_of_table(TableId::U32)).unwrap();
1✔
1488
        assert!(u32_height == profile.total.u32);
1✔
1489

1490
        println!("{profile}");
1✔
1491
    }
1✔
1492

1493
    #[test]
1494
    fn program_with_too_many_returns_crashes_vm_but_not_profiler() {
1✔
1495
        let program = triton_program! {
1✔
1496
            call foo return halt
1✔
1497
            foo: return
1✔
1498
        };
1✔
1499
        let_assert!(Err(err) = VM::profile(&program, [].into(), [].into()));
1✔
1500
        let_assert!(InstructionError::JumpStackIsEmpty = err.source);
1✔
1501
    }
1✔
1502

1503
    #[proptest]
256✔
1504
    fn from_various_types_to_public_input(#[strategy(arb())] tokens: Vec<BFieldElement>) {
1✔
1505
        let public_input = PublicInput::new(tokens.clone());
1506

1507
        assert!(public_input == tokens.clone().into());
1508
        assert!(public_input == (&tokens).into());
1509
        assert!(public_input == tokens[..].into());
1510
        assert!(public_input == (&tokens[..]).into());
1511

1512
        assert!(PublicInput::new(vec![]) == [].into());
1513
    }
1514

1515
    #[proptest]
256✔
1516
    fn from_various_types_to_non_determinism(#[strategy(arb())] tokens: Vec<BFieldElement>) {
1✔
1517
        let non_determinism = NonDeterminism::new(tokens.clone());
1518

1519
        assert!(non_determinism == tokens.clone().into());
1520
        assert!(non_determinism == tokens[..].into());
1521
        assert!(non_determinism == (&tokens[..]).into());
1522

1523
        assert!(NonDeterminism::new(vec![]) == [].into());
1524
    }
1525

1526
    #[test]
1527
    fn initialise_table() {
1✔
1528
        let program = GREATEST_COMMON_DIVISOR.clone();
1✔
1529
        let stdin = PublicInput::from([42, 56].map(|b| bfe!(b)));
2✔
1530
        let secret_in = NonDeterminism::default();
1✔
1531
        VM::trace_execution(&program, stdin, secret_in).unwrap();
1✔
1532
    }
1✔
1533

1534
    #[test]
1535
    fn run_tvm_gcd() {
1✔
1536
        let program = GREATEST_COMMON_DIVISOR.clone();
1✔
1537
        let stdin = PublicInput::from([42, 56].map(|b| bfe!(b)));
2✔
1538
        let secret_in = NonDeterminism::default();
1✔
1539
        let_assert!(Ok(stdout) = VM::run(&program, stdin, secret_in));
1✔
1540

1541
        let output = stdout.iter().map(|o| format!("{o}")).join(", ");
1✔
1542
        println!("VM output: [{output}]");
1✔
1543

1✔
1544
        assert!(bfe!(14) == stdout[0]);
1✔
1545
    }
1✔
1546

1547
    #[test]
1548
    fn crash_triton_vm_and_print_vm_error() {
1✔
1549
        let crashing_program = triton_program!(push 2 assert halt);
1✔
1550
        let_assert!(Err(err) = VM::run(&crashing_program, [].into(), [].into()));
1✔
1551
        println!("{err}");
1✔
1552
    }
1✔
1553

1554
    pub(crate) fn test_program_hash_nop_nop_lt() -> ProgramAndInput {
2✔
1555
        let push_5_zeros = triton_asm![push 0; 5];
2✔
1556
        let program = triton_program! {
2✔
1557
            {&push_5_zeros} hash
2✔
1558
            nop
2✔
1559
            {&push_5_zeros} hash
2✔
1560
            nop nop
2✔
1561
            {&push_5_zeros} hash
2✔
1562
            push 3 push 2 lt assert
2✔
1563
            halt
2✔
1564
        };
2✔
1565
        ProgramAndInput::new(program)
2✔
1566
    }
2✔
1567

1568
    pub(crate) fn test_program_for_halt() -> ProgramAndInput {
6✔
1569
        ProgramAndInput::new(triton_program!(halt))
6✔
1570
    }
6✔
1571

1572
    pub(crate) fn test_program_for_push_pop_dup_swap_nop() -> ProgramAndInput {
1✔
1573
        ProgramAndInput::new(triton_program!(
1✔
1574
            push 1 push 2 pop 1 assert
1✔
1575
            push 1 dup  0 assert assert
1✔
1576
            push 1 push 2 swap 1 assert pop 1
1✔
1577
            nop nop nop halt
1✔
1578
        ))
1✔
1579
    }
1✔
1580

1581
    pub(crate) fn test_program_for_divine() -> ProgramAndInput {
1✔
1582
        ProgramAndInput::new(triton_program!(divine 1 assert halt)).with_non_determinism([bfe!(1)])
1✔
1583
    }
1✔
1584

1585
    pub(crate) fn test_program_for_skiz() -> ProgramAndInput {
1✔
1586
        ProgramAndInput::new(triton_program!(push 1 skiz push 0 skiz assert push 1 skiz halt))
1✔
1587
    }
1✔
1588

1589
    pub(crate) fn test_program_for_call_recurse_return() -> ProgramAndInput {
5✔
1590
        ProgramAndInput::new(triton_program!(
5✔
1591
            push 2
5✔
1592
            call label
5✔
1593
            pop 1 halt
5✔
1594
            label:
5✔
1595
                push -1 add dup 0
5✔
1596
                skiz
5✔
1597
                    recurse
5✔
1598
                return
5✔
1599
        ))
5✔
1600
    }
5✔
1601

1602
    pub(crate) fn test_program_for_recurse_or_return() -> ProgramAndInput {
1✔
1603
        ProgramAndInput::new(triton_program! {
1✔
1604
            push 5 swap 5
1✔
1605
            push 0 swap 5
1✔
1606
            call label
1✔
1607
            halt
1✔
1608
            label:
1✔
1609
                swap 5
1✔
1610
                push 1 add
1✔
1611
                swap 5
1✔
1612
                recurse_or_return
1✔
1613
        })
1✔
1614
    }
1✔
1615

1616
    /// Test helper for property testing instruction `recurse_or_return`.
1617
    ///
1618
    /// The [assembled](Self::assemble) program
1619
    /// - sets up a loop counter,
1620
    /// - populates ST6 with some “iteration terminator”,
1621
    /// - reads successive elements from standard input, and
1622
    /// - compares them to the iteration terminator using `recurse_or_return`.
1623
    ///
1624
    /// The program halts after the loop has run for the expected number of
1625
    /// iterations, crashing the VM if the number of iterations does not match
1626
    /// expectations.
1627
    #[derive(Debug, Clone, Eq, PartialEq, test_strategy::Arbitrary)]
828✔
1628
    pub struct ProgramForRecurseOrReturn {
1629
        #[strategy(arb())]
1630
        iteration_terminator: BFieldElement,
2✔
1631

1632
        #[strategy(arb())]
1633
        #[filter(#other_iterator_values.iter().all(|&v| v != #iteration_terminator))]
312✔
1634
        other_iterator_values: Vec<BFieldElement>,
2✔
1635
    }
1636

1637
    impl ProgramForRecurseOrReturn {
1638
        pub fn assemble(self) -> ProgramAndInput {
276✔
1639
            let expected_num_iterations = self.other_iterator_values.len() + 1;
276✔
1640

276✔
1641
            let program = triton_program! {
276✔
1642
                // set up iteration counter
276✔
1643
                push 0 hint iteration_counter = stack[0]
276✔
1644

276✔
1645
                // set up termination condition
276✔
1646
                push {self.iteration_terminator}
276✔
1647
                swap 6
276✔
1648

276✔
1649
                call iteration_loop
276✔
1650

276✔
1651
                // check iteration counter
276✔
1652
                push {expected_num_iterations}
276✔
1653
                eq assert
276✔
1654
                halt
276✔
1655

276✔
1656
                iteration_loop:
276✔
1657
                    // increment iteration counter
276✔
1658
                    push 1 add
276✔
1659

276✔
1660
                    // check loop termination
276✔
1661
                    swap 5
276✔
1662
                    pop 1
276✔
1663
                    read_io 1
276✔
1664
                    swap 5
276✔
1665
                    recurse_or_return
276✔
1666
            };
276✔
1667

276✔
1668
            let mut input = self.other_iterator_values;
276✔
1669
            input.push(self.iteration_terminator);
276✔
1670
            ProgramAndInput::new(program).with_input(input)
276✔
1671
        }
276✔
1672
    }
1673

1674
    #[proptest]
256✔
1675
    fn property_based_recurse_or_return_program_sanity_check(program: ProgramForRecurseOrReturn) {
1676
        program.assemble().run()?;
1677
    }
1678

1679
    #[test]
1680
    fn vm_crashes_when_executing_recurse_or_return_with_empty_jump_stack() {
1✔
1681
        let program = triton_program!(recurse_or_return halt);
1✔
1682
        let_assert!(
1✔
1683
            Err(err) = VM::run(&program, PublicInput::default(), NonDeterminism::default())
1✔
1684
        );
1685
        assert!(InstructionError::JumpStackIsEmpty == err.source);
1✔
1686
    }
1✔
1687

1688
    pub(crate) fn test_program_for_write_mem_read_mem() -> ProgramAndInput {
1✔
1689
        ProgramAndInput::new(triton_program! {
1✔
1690
            push 3 push 1 push 2    // _ 3 1 2
1✔
1691
            push 7                  // _ 3 1 2 7
1✔
1692
            write_mem 3             // _ 10
1✔
1693
            push -1 add             // _ 9
1✔
1694
            read_mem 2              // _ 3 1 7
1✔
1695
            pop 1                   // _ 3 1
1✔
1696
            assert halt             // _ 3
1✔
1697
        })
1✔
1698
    }
1✔
1699

1700
    pub(crate) fn test_program_for_hash() -> ProgramAndInput {
1✔
1701
        let program = triton_program!(
1✔
1702
            push 0 // filler to keep the OpStack large enough throughout the program
1✔
1703
            push 0 push 0 push 1 push 2 push 3
1✔
1704
            hash
1✔
1705
            read_io 1 eq assert halt
1✔
1706
        );
1✔
1707
        let hash_input = bfe_array![3, 2, 1, 0, 0, 0, 0, 0, 0, 0];
1✔
1708
        let digest = Tip5::hash_10(&hash_input);
1✔
1709
        ProgramAndInput::new(program).with_input(&digest[..=0])
1✔
1710
    }
1✔
1711

1712
    /// Helper function that returns code to push a digest to the top of the stack
1713
    fn push_digest_to_stack_tasm(Digest([d0, d1, d2, d3, d4]): Digest) -> Vec<LabelledInstruction> {
16✔
1714
        triton_asm!(push {d4} push {d3} push {d2} push {d1} push {d0})
16✔
1715
    }
16✔
1716

1717
    pub(crate) fn test_program_for_merkle_step_right_sibling() -> ProgramAndInput {
2✔
1718
        let accumulator_digest = Digest::new(bfe_array![2, 12, 22, 32, 42]);
2✔
1719
        let divined_digest = Digest::new(bfe_array![10, 11, 12, 13, 14]);
2✔
1720
        let expected_digest = Tip5::hash_pair(divined_digest, accumulator_digest);
2✔
1721
        let merkle_tree_node_index = 3;
2✔
1722
        let program = triton_program!(
2✔
1723
            push {merkle_tree_node_index}
2✔
1724
            {&push_digest_to_stack_tasm(accumulator_digest)}
2✔
1725
            merkle_step
2✔
1726

2✔
1727
            {&push_digest_to_stack_tasm(expected_digest)}
2✔
1728
            assert_vector pop 5
2✔
1729
            assert halt
2✔
1730
        );
2✔
1731

2✔
1732
        let non_determinism = NonDeterminism::default().with_digests(vec![divined_digest]);
2✔
1733
        ProgramAndInput::new(program).with_non_determinism(non_determinism)
2✔
1734
    }
2✔
1735

1736
    pub(crate) fn test_program_for_merkle_step_left_sibling() -> ProgramAndInput {
2✔
1737
        let accumulator_digest = Digest::new(bfe_array![2, 12, 22, 32, 42]);
2✔
1738
        let divined_digest = Digest::new(bfe_array![10, 11, 12, 13, 14]);
2✔
1739
        let expected_digest = Tip5::hash_pair(accumulator_digest, divined_digest);
2✔
1740
        let merkle_tree_node_index = 2;
2✔
1741
        let program = triton_program!(
2✔
1742
            push {merkle_tree_node_index}
2✔
1743
            {&push_digest_to_stack_tasm(accumulator_digest)}
2✔
1744
            merkle_step
2✔
1745

2✔
1746
            {&push_digest_to_stack_tasm(expected_digest)}
2✔
1747
            assert_vector pop 5
2✔
1748
            assert halt
2✔
1749
        );
2✔
1750

2✔
1751
        let non_determinism = NonDeterminism::default().with_digests(vec![divined_digest]);
2✔
1752
        ProgramAndInput::new(program).with_non_determinism(non_determinism)
2✔
1753
    }
2✔
1754

1755
    pub(crate) fn test_program_for_merkle_step_mem_right_sibling() -> ProgramAndInput {
2✔
1756
        let accumulator_digest = Digest::new(bfe_array![2, 12, 22, 32, 42]);
2✔
1757
        let sibling_digest = Digest::new(bfe_array![10, 11, 12, 13, 14]);
2✔
1758
        let expected_digest = Tip5::hash_pair(sibling_digest, accumulator_digest);
2✔
1759
        let auth_path_address = 1337;
2✔
1760
        let merkle_tree_node_index = 3;
2✔
1761
        let program = triton_program!(
2✔
1762
            push {auth_path_address}
2✔
1763
            push 0 // dummy
2✔
1764
            push {merkle_tree_node_index}
2✔
1765
            {&push_digest_to_stack_tasm(accumulator_digest)}
2✔
1766
            merkle_step_mem
2✔
1767

2✔
1768
            {&push_digest_to_stack_tasm(expected_digest)}
2✔
1769
            assert_vector pop 5
2✔
1770
            assert halt
2✔
1771
        );
2✔
1772

2✔
1773
        let ram = (auth_path_address..)
2✔
1774
            .map(BFieldElement::new)
2✔
1775
            .zip(sibling_digest.values())
2✔
1776
            .collect::<HashMap<_, _>>();
2✔
1777
        let non_determinism = NonDeterminism::default().with_ram(ram);
2✔
1778
        ProgramAndInput::new(program).with_non_determinism(non_determinism)
2✔
1779
    }
2✔
1780

1781
    pub(crate) fn test_program_for_merkle_step_mem_left_sibling() -> ProgramAndInput {
2✔
1782
        let accumulator_digest = Digest::new(bfe_array![2, 12, 22, 32, 42]);
2✔
1783
        let sibling_digest = Digest::new(bfe_array![10, 11, 12, 13, 14]);
2✔
1784
        let expected_digest = Tip5::hash_pair(accumulator_digest, sibling_digest);
2✔
1785
        let auth_path_address = 1337;
2✔
1786
        let merkle_tree_node_index = 2;
2✔
1787
        let program = triton_program!(
2✔
1788
            push {auth_path_address}
2✔
1789
            push 0 // dummy
2✔
1790
            push {merkle_tree_node_index}
2✔
1791
            {&push_digest_to_stack_tasm(accumulator_digest)}
2✔
1792
            merkle_step_mem
2✔
1793

2✔
1794
            {&push_digest_to_stack_tasm(expected_digest)}
2✔
1795
            assert_vector pop 5
2✔
1796
            assert halt
2✔
1797
        );
2✔
1798

2✔
1799
        let ram = (auth_path_address..)
2✔
1800
            .map(BFieldElement::new)
2✔
1801
            .zip(sibling_digest.values())
2✔
1802
            .collect::<HashMap<_, _>>();
2✔
1803
        let non_determinism = NonDeterminism::default().with_ram(ram);
2✔
1804
        ProgramAndInput::new(program).with_non_determinism(non_determinism)
2✔
1805
    }
2✔
1806

1807
    /// Test helper for property-testing instruction `merkle_step_mem` in a
1808
    /// meaningful context, namely, using [`MERKLE_TREE_UPDATE`].
1809
    #[derive(Debug, Clone, test_strategy::Arbitrary)]
1,430✔
1810
    pub struct ProgramForMerkleTreeUpdate {
1811
        leaved_merkle_tree: LeavedMerkleTreeTestData,
1812

1813
        #[strategy(0..#leaved_merkle_tree.revealed_indices.len())]
1814
        #[map(|i| #leaved_merkle_tree.revealed_indices[i])]
286✔
1815
        revealed_leafs_index: usize,
1816

1817
        #[strategy(arb())]
1818
        new_leaf: Digest,
3✔
1819

1820
        #[strategy(arb())]
1821
        auth_path_address: BFieldElement,
3✔
1822
    }
1823

1824
    impl ProgramForMerkleTreeUpdate {
1825
        pub fn assemble(self) -> ProgramAndInput {
286✔
1826
            let auth_path = self.authentication_path();
286✔
1827
            let leaf_index = self.revealed_leafs_index;
286✔
1828
            let merkle_tree = self.leaved_merkle_tree.merkle_tree;
286✔
1829

286✔
1830
            let ram = (self.auth_path_address.value()..)
286✔
1831
                .map(BFieldElement::new)
286✔
1832
                .zip(auth_path.iter().flat_map(|digest| digest.values()))
1,552✔
1833
                .collect::<HashMap<_, _>>();
286✔
1834
            let non_determinism = NonDeterminism::default().with_ram(ram);
286✔
1835

286✔
1836
            let old_leaf = Digest::from(self.leaved_merkle_tree.leaves[leaf_index]);
286✔
1837
            let old_root = merkle_tree.root();
286✔
1838
            let mut public_input = vec![
286✔
1839
                self.auth_path_address,
286✔
1840
                u64::try_from(leaf_index).unwrap().into(),
286✔
1841
                u64::try_from(merkle_tree.height()).unwrap().into(),
286✔
1842
            ];
286✔
1843
            public_input.extend(old_leaf.reversed().values());
286✔
1844
            public_input.extend(old_root.reversed().values());
286✔
1845
            public_input.extend(self.new_leaf.reversed().values());
286✔
1846

286✔
1847
            ProgramAndInput::new(MERKLE_TREE_UPDATE.clone())
286✔
1848
                .with_input(public_input)
286✔
1849
                .with_non_determinism(non_determinism)
286✔
1850
        }
286✔
1851

1852
        /// Checks whether the [`ProgramAndInput`] generated through [`Self::assemble`]
1853
        /// can
1854
        /// - be executed without crashing the VM, and
1855
        /// - produces the correct output.
1856
        #[must_use]
1857
        pub fn is_integral(&self) -> bool {
256✔
1858
            let inclusion_proof = MerkleTreeInclusionProof {
256✔
1859
                tree_height: self.leaved_merkle_tree.merkle_tree.height(),
256✔
1860
                indexed_leafs: vec![(self.revealed_leafs_index, self.new_leaf)],
256✔
1861
                authentication_structure: self.authentication_path(),
256✔
1862
            };
256✔
1863

256✔
1864
            let new_root = self.clone().assemble().run().unwrap();
256✔
1865
            let new_root = Digest(new_root.try_into().unwrap());
256✔
1866
            inclusion_proof.verify(new_root)
256✔
1867
        }
256✔
1868

1869
        /// The authentication path for the leaf at `self.revealed_leafs_index`.
1870
        /// Independent of the leaf's value, _i.e._, is up to date even one the leaf
1871
        /// has been updated.
1872
        fn authentication_path(&self) -> Vec<Digest> {
542✔
1873
            self.leaved_merkle_tree
542✔
1874
                .merkle_tree
542✔
1875
                .authentication_structure(&[self.revealed_leafs_index])
542✔
1876
                .unwrap()
542✔
1877
        }
542✔
1878
    }
1879

1880
    pub(crate) fn test_program_for_assert_vector() -> ProgramAndInput {
1✔
1881
        ProgramAndInput::new(triton_program!(
1✔
1882
            push 1 push 2 push 3 push 4 push 5
1✔
1883
            push 1 push 2 push 3 push 4 push 5
1✔
1884
            assert_vector halt
1✔
1885
        ))
1✔
1886
    }
1✔
1887

1888
    pub(crate) fn test_program_for_sponge_instructions() -> ProgramAndInput {
1✔
1889
        let push_10_zeros = triton_asm![push 0; 10];
1✔
1890
        ProgramAndInput::new(triton_program!(
1✔
1891
            sponge_init
1✔
1892
            {&push_10_zeros} sponge_absorb
1✔
1893
            {&push_10_zeros} sponge_absorb
1✔
1894
            sponge_squeeze halt
1✔
1895
        ))
1✔
1896
    }
1✔
1897

1898
    pub(crate) fn test_program_for_sponge_instructions_2() -> ProgramAndInput {
1✔
1899
        let push_5_zeros = triton_asm![push 0; 5];
1✔
1900
        let program = triton_program! {
1✔
1901
            sponge_init
1✔
1902
            sponge_squeeze sponge_absorb
1✔
1903
            {&push_5_zeros} hash
1✔
1904
            sponge_squeeze sponge_absorb
1✔
1905
            halt
1✔
1906
        };
1✔
1907
        ProgramAndInput::new(program)
1✔
1908
    }
1✔
1909

1910
    pub(crate) fn test_program_for_many_sponge_instructions() -> ProgramAndInput {
1✔
1911
        let push_5_zeros = triton_asm![push 0; 5];
1✔
1912
        let push_10_zeros = triton_asm![push 0; 10];
1✔
1913
        let program = triton_program! {         //  elements on stack
1✔
1914
            sponge_init                         //  0
1✔
1915
            sponge_squeeze sponge_absorb        //  0
1✔
1916
            {&push_10_zeros} sponge_absorb      //  0
1✔
1917
            {&push_10_zeros} sponge_absorb      //  0
1✔
1918
            sponge_squeeze sponge_squeeze       // 20
1✔
1919
            sponge_squeeze sponge_absorb        // 20
1✔
1920
            sponge_init sponge_init sponge_init // 20
1✔
1921
            sponge_absorb                       // 10
1✔
1922
            sponge_init                         // 10
1✔
1923
            sponge_squeeze sponge_squeeze       // 30
1✔
1924
            sponge_init sponge_squeeze          // 40
1✔
1925
            {&push_5_zeros} hash sponge_absorb  // 30
1✔
1926
            {&push_5_zeros} hash sponge_squeeze // 40
1✔
1927
            {&push_5_zeros} hash sponge_absorb  // 30
1✔
1928
            {&push_5_zeros} hash sponge_squeeze // 40
1✔
1929
            sponge_init                         // 40
1✔
1930
            sponge_absorb sponge_absorb         // 20
1✔
1931
            sponge_absorb sponge_absorb         //  0
1✔
1932
            {&push_10_zeros} sponge_absorb      //  0
1✔
1933
            {&push_10_zeros} sponge_absorb      //  0
1✔
1934
            {&push_10_zeros} sponge_absorb      //  0
1✔
1935
            halt
1✔
1936
        };
1✔
1937
        ProgramAndInput::new(program)
1✔
1938
    }
1✔
1939

1940
    pub(crate) fn property_based_test_program_for_assert_vector() -> ProgramAndInput {
1✔
1941
        let mut rng = ThreadRng::default();
1✔
1942
        let st: [BFieldElement; 5] = rng.gen();
1✔
1943

1✔
1944
        let program = triton_program!(
1✔
1945
            push {st[0]} push {st[1]} push {st[2]} push {st[3]} push {st[4]}
1✔
1946
            read_io 5 assert_vector halt
1✔
1947
        );
1✔
1948

1✔
1949
        ProgramAndInput::new(program).with_input(st)
1✔
1950
    }
1✔
1951

1952
    /// Test helper for [`ProgramForSpongeAndHashInstructions`].
1953
    #[derive(Debug, Copy, Clone, Eq, PartialEq, EnumCount, EnumIter, test_strategy::Arbitrary)]
12,794✔
1954
    enum SpongeAndHashInstructions {
1955
        SpongeInit,
1956
        SpongeAbsorb(#[strategy(arb())] [BFieldElement; tip5::RATE]),
2✔
1957
        SpongeAbsorbMem(#[strategy(arb())] BFieldElement),
2✔
1958
        SpongeSqueeze,
1959
        Hash(#[strategy(arb())] [BFieldElement; tip5::RATE]),
2✔
1960
    }
1961

1962
    impl SpongeAndHashInstructions {
1963
        fn to_vm_instruction_sequence(self) -> Vec<Instruction> {
12,794✔
1964
            let push_array = |a: [_; tip5::RATE]| a.into_iter().rev().map(Push).collect_vec();
12,794✔
1965

1966
            match self {
12,794✔
1967
                Self::SpongeInit => vec![SpongeInit],
2,595✔
1968
                Self::SpongeAbsorb(input) => [push_array(input), vec![SpongeAbsorb]].concat(),
2,611✔
1969
                Self::SpongeAbsorbMem(ram_pointer) => vec![Push(ram_pointer), SpongeAbsorbMem],
2,557✔
1970
                Self::SpongeSqueeze => vec![SpongeSqueeze],
2,542✔
1971
                Self::Hash(input) => [push_array(input), vec![Hash]].concat(),
2,489✔
1972
            }
1973
        }
12,794✔
1974

1975
        fn execute(self, sponge: &mut Tip5, ram: &HashMap<BFieldElement, BFieldElement>) {
12,794✔
1976
            let ram_read = |p| ram.get(&p).copied().unwrap_or_else(|| bfe!(0));
25,570✔
1977
            let ram_read_array = |p| {
12,794✔
1978
                let ram_reads = (0..tip5::RATE as u32).map(|i| ram_read(p + bfe!(i)));
25,570✔
1979
                ram_reads.collect_vec().try_into().unwrap()
2,557✔
1980
            };
2,557✔
1981

1982
            match self {
12,794✔
1983
                Self::SpongeInit => *sponge = Tip5::init(),
2,595✔
1984
                Self::SpongeAbsorb(input) => sponge.absorb(input),
2,611✔
1985
                Self::SpongeAbsorbMem(ram_pointer) => sponge.absorb(ram_read_array(ram_pointer)),
2,557✔
1986
                Self::SpongeSqueeze => _ = sponge.squeeze(),
2,542✔
1987
                Self::Hash(_) => (), // no effect on Sponge
2,489✔
1988
            }
1989
        }
12,794✔
1990
    }
1991

1992
    /// Test helper for arbitrary sequences of hashing-related instructions.
1993
    #[derive(Debug, Clone, Eq, PartialEq, test_strategy::Arbitrary)]
518✔
1994
    pub struct ProgramForSpongeAndHashInstructions {
1995
        instructions: Vec<SpongeAndHashInstructions>,
1996

1997
        #[strategy(arb())]
1998
        ram: HashMap<BFieldElement, BFieldElement>,
2✔
1999
    }
2000

2001
    impl ProgramForSpongeAndHashInstructions {
2002
        pub fn assemble(self) -> ProgramAndInput {
259✔
2003
            let mut sponge = Tip5::init();
259✔
2004
            for instruction in &self.instructions {
13,053✔
2005
                instruction.execute(&mut sponge, &self.ram);
12,794✔
2006
            }
12,794✔
2007
            let expected_rate = sponge.squeeze();
259✔
2008
            let non_determinism = NonDeterminism::default().with_ram(self.ram);
259✔
2009

259✔
2010
            let sponge_and_hash_instructions = self
259✔
2011
                .instructions
259✔
2012
                .into_iter()
259✔
2013
                .flat_map(|i| i.to_vm_instruction_sequence())
12,794✔
2014
                .collect_vec();
259✔
2015
            let output_equality_assertions =
259✔
2016
                vec![triton_asm![read_io 1 eq assert]; tip5::RATE].concat();
259✔
2017

259✔
2018
            let program = triton_program! {
259✔
2019
                sponge_init
259✔
2020
                {&sponge_and_hash_instructions}
259✔
2021
                sponge_squeeze
259✔
2022
                {&output_equality_assertions}
259✔
2023
                halt
259✔
2024
            };
259✔
2025

259✔
2026
            ProgramAndInput::new(program)
259✔
2027
                .with_input(expected_rate)
259✔
2028
                .with_non_determinism(non_determinism)
259✔
2029
        }
259✔
2030
    }
2031

2032
    #[proptest]
256✔
2033
    fn property_based_sponge_and_hash_instructions_program_sanity_check(
2034
        program: ProgramForSpongeAndHashInstructions,
2035
    ) {
2036
        program.assemble().run()?;
2037
    }
2038

2039
    pub(crate) fn test_program_for_add_mul_invert() -> ProgramAndInput {
1✔
2040
        ProgramAndInput::new(triton_program!(
1✔
2041
            push  2 push -1 add assert
1✔
2042
            push -1 push -1 mul assert
1✔
2043
            push  5 addi -4 assert
1✔
2044
            push  3 dup 0 invert mul assert
1✔
2045
            halt
1✔
2046
        ))
1✔
2047
    }
1✔
2048

2049
    pub(crate) fn property_based_test_program_for_split() -> ProgramAndInput {
1✔
2050
        let mut rng = ThreadRng::default();
1✔
2051
        let st0 = rng.next_u64() % BFieldElement::P;
1✔
2052
        let hi = st0 >> 32;
1✔
2053
        let lo = st0 & 0xffff_ffff;
1✔
2054

1✔
2055
        let program =
1✔
2056
            triton_program!(push {st0} split read_io 1 eq assert read_io 1 eq assert halt);
1✔
2057
        ProgramAndInput::new(program).with_input(bfe_array![lo, hi])
1✔
2058
    }
1✔
2059

2060
    pub(crate) fn test_program_for_eq() -> ProgramAndInput {
1✔
2061
        let input = bfe_array![42];
1✔
2062
        ProgramAndInput::new(triton_program!(read_io 1 divine 1 eq assert halt))
1✔
2063
            .with_input(input)
1✔
2064
            .with_non_determinism(input)
1✔
2065
    }
1✔
2066

2067
    pub(crate) fn property_based_test_program_for_eq() -> ProgramAndInput {
1✔
2068
        let mut rng = ThreadRng::default();
1✔
2069
        let st0 = rng.next_u64() % BFieldElement::P;
1✔
2070
        let input = bfe_array![st0];
1✔
2071

1✔
2072
        let program =
1✔
2073
            triton_program!(push {st0} dup 0 read_io 1 eq assert dup 0 divine 1 eq assert halt);
1✔
2074
        ProgramAndInput::new(program)
1✔
2075
            .with_input(input)
1✔
2076
            .with_non_determinism(input)
1✔
2077
    }
1✔
2078

2079
    pub(crate) fn test_program_for_lsb() -> ProgramAndInput {
1✔
2080
        ProgramAndInput::new(triton_program!(
1✔
2081
            push 3 call lsb assert assert halt
1✔
2082
            lsb:
1✔
2083
                push 2 swap 1 div_mod return
1✔
2084
        ))
1✔
2085
    }
1✔
2086

2087
    pub(crate) fn property_based_test_program_for_lsb() -> ProgramAndInput {
1✔
2088
        let mut rng = ThreadRng::default();
1✔
2089
        let st0 = rng.next_u32();
1✔
2090
        let lsb = st0 % 2;
1✔
2091
        let st0_shift_right = st0 >> 1;
1✔
2092

1✔
2093
        let program = triton_program!(
1✔
2094
            push {st0} call lsb read_io 1 eq assert read_io 1 eq assert halt
1✔
2095
            lsb:
1✔
2096
                push 2 swap 1 div_mod return
1✔
2097
        );
1✔
2098
        ProgramAndInput::new(program).with_input([lsb, st0_shift_right].map(|b| bfe!(b)))
2✔
2099
    }
1✔
2100

2101
    pub(crate) fn test_program_0_lt_0() -> ProgramAndInput {
1✔
2102
        ProgramAndInput::new(triton_program!(push 0 push 0 lt halt))
1✔
2103
    }
1✔
2104

2105
    pub(crate) fn test_program_for_lt() -> ProgramAndInput {
1✔
2106
        ProgramAndInput::new(triton_program!(
1✔
2107
            push 5 push 2 lt assert push 2 push 5 lt push 0 eq assert halt
1✔
2108
        ))
1✔
2109
    }
1✔
2110

2111
    pub(crate) fn property_based_test_program_for_lt() -> ProgramAndInput {
1✔
2112
        let mut rng = ThreadRng::default();
1✔
2113

1✔
2114
        let st1_0 = rng.next_u32();
1✔
2115
        let st0_0 = rng.next_u32();
1✔
2116
        let result_0 = match st0_0 < st1_0 {
1✔
2117
            true => 1,
×
2118
            false => 0,
1✔
2119
        };
2120

2121
        let st1_1 = rng.next_u32();
1✔
2122
        let st0_1 = rng.next_u32();
1✔
2123
        let result_1 = match st0_1 < st1_1 {
1✔
2124
            true => 1,
1✔
UNCOV
2125
            false => 0,
×
2126
        };
2127

2128
        let program = triton_program!(
1✔
2129
            push {st1_0} push {st0_0} lt read_io 1 eq assert
1✔
2130
            push {st1_1} push {st0_1} lt read_io 1 eq assert halt
1✔
2131
        );
1✔
2132
        ProgramAndInput::new(program).with_input([result_0, result_1].map(|b| bfe!(b)))
2✔
2133
    }
1✔
2134

2135
    pub(crate) fn test_program_for_and() -> ProgramAndInput {
1✔
2136
        ProgramAndInput::new(triton_program!(
1✔
2137
            push 5 push 3 and assert push 12 push 5 and push 4 eq assert halt
1✔
2138
        ))
1✔
2139
    }
1✔
2140

2141
    pub(crate) fn property_based_test_program_for_and() -> ProgramAndInput {
1✔
2142
        let mut rng = ThreadRng::default();
1✔
2143

1✔
2144
        let st1_0 = rng.next_u32();
1✔
2145
        let st0_0 = rng.next_u32();
1✔
2146
        let result_0 = st0_0.bitand(st1_0);
1✔
2147

1✔
2148
        let st1_1 = rng.next_u32();
1✔
2149
        let st0_1 = rng.next_u32();
1✔
2150
        let result_1 = st0_1.bitand(st1_1);
1✔
2151

1✔
2152
        let program = triton_program!(
1✔
2153
            push {st1_0} push {st0_0} and read_io 1 eq assert
1✔
2154
            push {st1_1} push {st0_1} and read_io 1 eq assert halt
1✔
2155
        );
1✔
2156
        ProgramAndInput::new(program).with_input([result_0, result_1].map(|b| bfe!(b)))
2✔
2157
    }
1✔
2158

2159
    pub(crate) fn test_program_for_xor() -> ProgramAndInput {
1✔
2160
        ProgramAndInput::new(triton_program!(
1✔
2161
            push 7 push 6 xor assert push 5 push 12 xor push 9 eq assert halt
1✔
2162
        ))
1✔
2163
    }
1✔
2164

2165
    pub(crate) fn property_based_test_program_for_xor() -> ProgramAndInput {
1✔
2166
        let mut rng = ThreadRng::default();
1✔
2167

1✔
2168
        let st1_0 = rng.next_u32();
1✔
2169
        let st0_0 = rng.next_u32();
1✔
2170
        let result_0 = st0_0.bitxor(st1_0);
1✔
2171

1✔
2172
        let st1_1 = rng.next_u32();
1✔
2173
        let st0_1 = rng.next_u32();
1✔
2174
        let result_1 = st0_1.bitxor(st1_1);
1✔
2175

1✔
2176
        let program = triton_program!(
1✔
2177
            push {st1_0} push {st0_0} xor read_io 1 eq assert
1✔
2178
            push {st1_1} push {st0_1} xor read_io 1 eq assert halt
1✔
2179
        );
1✔
2180
        ProgramAndInput::new(program).with_input([result_0, result_1].map(|b| bfe!(b)))
2✔
2181
    }
1✔
2182

2183
    pub(crate) fn test_program_for_log2floor() -> ProgramAndInput {
1✔
2184
        ProgramAndInput::new(triton_program!(
1✔
2185
            push  1 log_2_floor push  0 eq assert
1✔
2186
            push  2 log_2_floor push  1 eq assert
1✔
2187
            push  3 log_2_floor push  1 eq assert
1✔
2188
            push  4 log_2_floor push  2 eq assert
1✔
2189
            push  7 log_2_floor push  2 eq assert
1✔
2190
            push  8 log_2_floor push  3 eq assert
1✔
2191
            push 15 log_2_floor push  3 eq assert
1✔
2192
            push 16 log_2_floor push  4 eq assert
1✔
2193
            push 31 log_2_floor push  4 eq assert
1✔
2194
            push 32 log_2_floor push  5 eq assert
1✔
2195
            push 33 log_2_floor push  5 eq assert
1✔
2196
            push 4294967295 log_2_floor push 31 eq assert halt
1✔
2197
        ))
1✔
2198
    }
1✔
2199

2200
    pub(crate) fn property_based_test_program_for_log2floor() -> ProgramAndInput {
1✔
2201
        let mut rng = ThreadRng::default();
1✔
2202

1✔
2203
        let st0_0 = rng.next_u32();
1✔
2204
        let l2f_0 = st0_0.ilog2();
1✔
2205

1✔
2206
        let st0_1 = rng.next_u32();
1✔
2207
        let l2f_1 = st0_1.ilog2();
1✔
2208

1✔
2209
        let program = triton_program!(
1✔
2210
            push {st0_0} log_2_floor read_io 1 eq assert
1✔
2211
            push {st0_1} log_2_floor read_io 1 eq assert halt
1✔
2212
        );
1✔
2213
        ProgramAndInput::new(program).with_input([l2f_0, l2f_1].map(|b| bfe!(b)))
2✔
2214
    }
1✔
2215

2216
    pub(crate) fn test_program_for_pow() -> ProgramAndInput {
1✔
2217
        ProgramAndInput::new(triton_program!(
1✔
2218
            // push <exponent: u32> push <base: BFE> pow push <result: BFE> eq assert
1✔
2219
            push  0 push 0 pow push    1 eq assert
1✔
2220
            push  0 push 1 pow push    1 eq assert
1✔
2221
            push  0 push 2 pow push    1 eq assert
1✔
2222
            push  1 push 0 pow push    0 eq assert
1✔
2223
            push  2 push 0 pow push    0 eq assert
1✔
2224
            push  2 push 5 pow push   25 eq assert
1✔
2225
            push  5 push 2 pow push   32 eq assert
1✔
2226
            push 10 push 2 pow push 1024 eq assert
1✔
2227
            push  3 push 3 pow push   27 eq assert
1✔
2228
            push  3 push 5 pow push  125 eq assert
1✔
2229
            push  9 push 7 pow push 40353607 eq assert
1✔
2230
            push 3040597274 push 05218640216028681988 pow push 11160453713534536216 eq assert
1✔
2231
            push 2378067562 push 13711477740065654150 pow push 06848017529532358230 eq assert
1✔
2232
            push  129856251 push 00218966585049330803 pow push 08283208434666229347 eq assert
1✔
2233
            push 1657936293 push 04999758396092641065 pow push 11426020017566937356 eq assert
1✔
2234
            push 3474149688 push 05702231339458623568 pow push 02862889945380025510 eq assert
1✔
2235
            push 2243935791 push 09059335263701504667 pow push 04293137302922963369 eq assert
1✔
2236
            push 1783029319 push 00037306102533222534 pow push 10002149917806048099 eq assert
1✔
2237
            push 3608140376 push 17716542154416103060 pow push 11885601801443303960 eq assert
1✔
2238
            push 1220084884 push 07207865095616988291 pow push 05544378138345942897 eq assert
1✔
2239
            push 3539668245 push 13491612301110950186 pow push 02612675697712040250 eq assert
1✔
2240
            halt
1✔
2241
        ))
1✔
2242
    }
1✔
2243

2244
    pub(crate) fn property_based_test_program_for_pow() -> ProgramAndInput {
1✔
2245
        let mut rng = ThreadRng::default();
1✔
2246

1✔
2247
        let base_0: BFieldElement = rng.gen();
1✔
2248
        let exp_0 = rng.next_u32();
1✔
2249
        let result_0 = base_0.mod_pow_u32(exp_0);
1✔
2250

1✔
2251
        let base_1: BFieldElement = rng.gen();
1✔
2252
        let exp_1 = rng.next_u32();
1✔
2253
        let result_1 = base_1.mod_pow_u32(exp_1);
1✔
2254

1✔
2255
        let program = triton_program!(
1✔
2256
            push {exp_0} push {base_0} pow read_io 1 eq assert
1✔
2257
            push {exp_1} push {base_1} pow read_io 1 eq assert halt
1✔
2258
        );
1✔
2259
        ProgramAndInput::new(program).with_input([result_0, result_1])
1✔
2260
    }
1✔
2261

2262
    pub(crate) fn test_program_for_div_mod() -> ProgramAndInput {
1✔
2263
        ProgramAndInput::new(triton_program!(push 2 push 3 div_mod assert assert halt))
1✔
2264
    }
1✔
2265

2266
    pub(crate) fn property_based_test_program_for_div_mod() -> ProgramAndInput {
1✔
2267
        let mut rng = ThreadRng::default();
1✔
2268

1✔
2269
        let denominator = rng.next_u32();
1✔
2270
        let numerator = rng.next_u32();
1✔
2271
        let quotient = numerator / denominator;
1✔
2272
        let remainder = numerator % denominator;
1✔
2273

1✔
2274
        let program = triton_program!(
1✔
2275
            push {denominator} push {numerator} div_mod read_io 1 eq assert read_io 1 eq assert halt
1✔
2276
        );
1✔
2277
        ProgramAndInput::new(program).with_input([remainder, quotient].map(|b| bfe!(b)))
2✔
2278
    }
1✔
2279

2280
    pub(crate) fn test_program_for_starting_with_pop_count() -> ProgramAndInput {
1✔
2281
        ProgramAndInput::new(triton_program!(pop_count dup 0 push 0 eq assert halt))
1✔
2282
    }
1✔
2283

2284
    pub(crate) fn test_program_for_pop_count() -> ProgramAndInput {
1✔
2285
        ProgramAndInput::new(triton_program!(push 10 pop_count push 2 eq assert halt))
1✔
2286
    }
1✔
2287

2288
    pub(crate) fn property_based_test_program_for_pop_count() -> ProgramAndInput {
1✔
2289
        let mut rng = ThreadRng::default();
1✔
2290
        let st0 = rng.next_u32();
1✔
2291
        let pop_count = st0.count_ones();
1✔
2292
        let program = triton_program!(push {st0} pop_count read_io 1 eq assert halt);
1✔
2293
        ProgramAndInput::new(program).with_input(bfe_array![pop_count])
1✔
2294
    }
1✔
2295

2296
    pub(crate) fn property_based_test_program_for_is_u32() -> ProgramAndInput {
1✔
2297
        let mut rng = ThreadRng::default();
1✔
2298
        let st0_u32 = rng.next_u32();
1✔
2299
        let st0_not_u32 = (u64::from(rng.next_u32()) << 32) + u64::from(rng.next_u32());
1✔
2300
        let program = triton_program!(
1✔
2301
            push {st0_u32} call is_u32 assert
1✔
2302
            push {st0_not_u32} call is_u32 push 0 eq assert halt
1✔
2303
            is_u32:
1✔
2304
                 split pop 1 push 0 eq return
1✔
2305
        );
1✔
2306
        ProgramAndInput::new(program)
1✔
2307
    }
1✔
2308

2309
    pub(crate) fn property_based_test_program_for_random_ram_access() -> ProgramAndInput {
2✔
2310
        let mut rng = ThreadRng::default();
2✔
2311
        let num_memory_accesses = rng.gen_range(10..50);
2✔
2312
        let memory_addresses: Vec<BFieldElement> = random_elements(num_memory_accesses);
2✔
2313
        let mut memory_values: Vec<BFieldElement> = random_elements(num_memory_accesses);
2✔
2314
        let mut instructions = vec![];
2✔
2315

2316
        // Read some memory before first write to ensure that the memory is initialized with 0s.
2317
        // Not all addresses are read to have different access patterns:
2318
        // - Some addresses are read before written to.
2319
        // - Other addresses are written to before read.
2320
        for address in memory_addresses.iter().take(num_memory_accesses / 4) {
18✔
2321
            instructions.extend(triton_asm!(push {address} read_mem 1 pop 1 push 0 eq assert));
18✔
2322
        }
18✔
2323

2324
        // Write everything to RAM.
2325
        for (address, value) in memory_addresses.iter().zip_eq(memory_values.iter()) {
76✔
2326
            instructions.extend(triton_asm!(push {value} push {address} write_mem 1 pop 1));
76✔
2327
        }
76✔
2328

2329
        // Read back in random order and check that the values did not change.
2330
        let mut reading_permutation = (0..num_memory_accesses).collect_vec();
2✔
2331
        for i in 0..num_memory_accesses {
76✔
2332
            let j = rng.gen_range(0..num_memory_accesses);
76✔
2333
            reading_permutation.swap(i, j);
76✔
2334
        }
76✔
2335
        for idx in reading_permutation {
78✔
2336
            let address = memory_addresses[idx];
76✔
2337
            let value = memory_values[idx];
76✔
2338
            instructions
76✔
2339
                .extend(triton_asm!(push {address} read_mem 1 pop 1 push {value} eq assert));
76✔
2340
        }
76✔
2341

2342
        // Overwrite half the values with new ones.
2343
        let mut writing_permutation = (0..num_memory_accesses).collect_vec();
2✔
2344
        for i in 0..num_memory_accesses {
76✔
2345
            let j = rng.gen_range(0..num_memory_accesses);
76✔
2346
            writing_permutation.swap(i, j);
76✔
2347
        }
76✔
2348
        for idx in 0..num_memory_accesses / 2 {
37✔
2349
            let address = memory_addresses[writing_permutation[idx]];
37✔
2350
            let new_memory_value = rng.gen();
37✔
2351
            memory_values[writing_permutation[idx]] = new_memory_value;
37✔
2352
            instructions
37✔
2353
                .extend(triton_asm!(push {new_memory_value} push {address} write_mem 1 pop 1));
37✔
2354
        }
37✔
2355

2356
        // Read back all, i.e., unchanged and overwritten values in (different from before) random
2357
        // order and check that the values did not change.
2358
        let mut reading_permutation = (0..num_memory_accesses).collect_vec();
2✔
2359
        for i in 0..num_memory_accesses {
76✔
2360
            let j = rng.gen_range(0..num_memory_accesses);
76✔
2361
            reading_permutation.swap(i, j);
76✔
2362
        }
76✔
2363
        for idx in reading_permutation {
78✔
2364
            let address = memory_addresses[idx];
76✔
2365
            let value = memory_values[idx];
76✔
2366
            instructions
76✔
2367
                .extend(triton_asm!(push {address} read_mem 1 pop 1 push {value} eq assert));
76✔
2368
        }
76✔
2369

2370
        let program = triton_program! { { &instructions } halt };
2✔
2371
        ProgramAndInput::new(program)
2✔
2372
    }
2✔
2373

2374
    /// Sanity check for the relatively complex property-based test for random RAM access.
2375
    #[test]
2376
    fn run_dont_prove_property_based_test_for_random_ram_access() {
1✔
2377
        let source_code_and_input = property_based_test_program_for_random_ram_access();
1✔
2378
        source_code_and_input.run().unwrap();
1✔
2379
    }
1✔
2380

2381
    #[test]
2382
    fn can_compute_dot_product_from_uninitialized_ram() {
1✔
2383
        let program = triton_program!(xx_dot_step xb_dot_step halt);
1✔
2384
        VM::run(&program, PublicInput::default(), NonDeterminism::default()).unwrap();
1✔
2385
    }
1✔
2386

2387
    pub(crate) fn property_based_test_program_for_xx_dot_step() -> ProgramAndInput {
2✔
2388
        let mut rng = ThreadRng::default();
2✔
2389
        let n = rng.gen_range(0..10);
2✔
2390

2✔
2391
        let push_xfe = |xfe: XFieldElement| {
8✔
2392
            let [c_0, c_1, c_2] = xfe.coefficients;
8✔
2393
            triton_asm! { push {c_2} push {c_1} push {c_0} }
8✔
2394
        };
8✔
2395
        let push_and_write_xfe = |xfe| {
6✔
2396
            let push_xfe = push_xfe(xfe);
6✔
2397
            triton_asm! {
6✔
2398
                {&push_xfe}
6✔
2399
                dup 3
6✔
2400
                write_mem 3
6✔
2401
                swap 1
6✔
2402
                pop 1
6✔
2403
            }
6✔
2404
        };
6✔
2405
        let into_write_instructions = |elements: Vec<_>| {
4✔
2406
            elements
4✔
2407
                .into_iter()
4✔
2408
                .flat_map(push_and_write_xfe)
4✔
2409
                .collect_vec()
4✔
2410
        };
4✔
2411

2412
        let vector_one = (0..n).map(|_| rng.gen()).collect_vec();
3✔
2413
        let vector_two = (0..n).map(|_| rng.gen()).collect_vec();
3✔
2414
        let inner_product = vector_one
2✔
2415
            .iter()
2✔
2416
            .zip(&vector_two)
2✔
2417
            .map(|(&a, &b)| a * b)
3✔
2418
            .sum();
2✔
2419
        let push_inner_product = push_xfe(inner_product);
2✔
2420

2✔
2421
        let push_and_write_vector_one = into_write_instructions(vector_one);
2✔
2422
        let push_and_write_vector_two = into_write_instructions(vector_two);
2✔
2423
        let many_dotsteps = (0..n).map(|_| triton_instr!(xx_dot_step)).collect_vec();
3✔
2424

2✔
2425
        let code = triton_program! {
2✔
2426
            push 0
2✔
2427
            {&push_and_write_vector_one}
2✔
2428
            dup 0
2✔
2429
            {&push_and_write_vector_two}
2✔
2430
            pop 1
2✔
2431
            push 0
2✔
2432

2✔
2433
            {&many_dotsteps}
2✔
2434
            pop 2
2✔
2435
            push 0 push 0
2✔
2436

2✔
2437
            {&push_inner_product}
2✔
2438
            push 0 push 0
2✔
2439
            assert_vector
2✔
2440
            halt
2✔
2441
        };
2✔
2442
        ProgramAndInput::new(code)
2✔
2443
    }
2✔
2444

2445
    /// Sanity check
2446
    #[test]
2447
    fn run_dont_prove_property_based_test_program_for_xx_dot_step() {
1✔
2448
        let source_code_and_input = property_based_test_program_for_xx_dot_step();
1✔
2449
        source_code_and_input.run().unwrap();
1✔
2450
    }
1✔
2451

2452
    pub(crate) fn property_based_test_program_for_xb_dot_step() -> ProgramAndInput {
2✔
2453
        let mut rng = ThreadRng::default();
2✔
2454
        let n = rng.gen_range(0..10);
2✔
2455
        let push_xfe = |x: XFieldElement| {
2✔
2456
            triton_asm! {
2✔
2457
                push {x.coefficients[2]}
2✔
2458
                push {x.coefficients[1]}
2✔
2459
                push {x.coefficients[0]}
2✔
2460
            }
2✔
2461
        };
2✔
2462
        let push_and_write_xfe = |x: XFieldElement| {
6✔
2463
            triton_asm! {
6✔
2464
                push {x.coefficients[2]}
6✔
2465
                push {x.coefficients[1]}
6✔
2466
                push {x.coefficients[0]}
6✔
2467
                dup 3
6✔
2468
                write_mem 3
6✔
2469
                swap 1
6✔
2470
                pop 1
6✔
2471
            }
6✔
2472
        };
6✔
2473
        let push_and_write_bfe = |x: BFieldElement| {
6✔
2474
            triton_asm! {
6✔
2475
                push {x}
6✔
2476
                dup 1
6✔
2477
                write_mem 1
6✔
2478
                swap 1
6✔
2479
                pop 1
6✔
2480
            }
6✔
2481
        };
6✔
2482
        let vector_one = (0..n).map(|_| rng.gen::<XFieldElement>()).collect_vec();
6✔
2483
        let vector_two = (0..n).map(|_| rng.gen::<BFieldElement>()).collect_vec();
6✔
2484
        let inner_product = vector_one
2✔
2485
            .iter()
2✔
2486
            .zip(vector_two.iter())
2✔
2487
            .map(|(&a, &b)| a * b)
6✔
2488
            .sum::<XFieldElement>();
2✔
2489
        let push_and_write_vector_one = (0..n)
2✔
2490
            .flat_map(|i| push_and_write_xfe(vector_one[i]))
6✔
2491
            .collect_vec();
2✔
2492
        let push_and_write_vector_two = (0..n)
2✔
2493
            .flat_map(|i| push_and_write_bfe(vector_two[i]))
6✔
2494
            .collect_vec();
2✔
2495
        let push_inner_product = push_xfe(inner_product);
2✔
2496
        let many_dotsteps = (0..n).map(|_| triton_instr!(xb_dot_step)).collect_vec();
6✔
2497
        let code = triton_program! {
2✔
2498
            push 0
2✔
2499
            {&push_and_write_vector_one}
2✔
2500
            dup 0
2✔
2501
            {&push_and_write_vector_two}
2✔
2502
            pop 1
2✔
2503
            push 0
2✔
2504
            swap 1
2✔
2505
            {&many_dotsteps}
2✔
2506
            pop 1
2✔
2507
            pop 1
2✔
2508
            push 0
2✔
2509
            push 0
2✔
2510
            {&push_inner_product}
2✔
2511
            push 0
2✔
2512
            push 0
2✔
2513
            assert_vector
2✔
2514
            halt
2✔
2515
        };
2✔
2516
        ProgramAndInput::new(code)
2✔
2517
    }
2✔
2518

2519
    /// Sanity check
2520
    #[test]
2521
    fn run_dont_prove_property_based_test_program_for_xb_dot_step() {
1✔
2522
        let source_code_and_input = property_based_test_program_for_xb_dot_step();
1✔
2523
        source_code_and_input.run().unwrap();
1✔
2524
    }
1✔
2525

2526
    #[proptest]
256✔
2527
    fn negative_property_is_u32(
2528
        #[strategy(arb())]
2529
        #[filter(#st0.value() > u64::from(u32::MAX))]
256✔
2530
        st0: BFieldElement,
1✔
2531
    ) {
2532
        let program = triton_program!(
2533
            push {st0} call is_u32 assert halt
2534
            is_u32:
2535
                split pop 1 push 0 eq return
2536
        );
2537
        let program_and_input = ProgramAndInput::new(program);
2538
        let_assert!(Err(err) = program_and_input.run());
2539
        let_assert!(InstructionError::AssertionFailed = err.source);
2540
    }
2541

2542
    pub(crate) fn test_program_for_split() -> ProgramAndInput {
1✔
2543
        ProgramAndInput::new(triton_program!(
1✔
2544
            push -2 split push 4294967295 eq assert push 4294967294 eq assert
1✔
2545
            push -1 split push 0 eq assert push 4294967295 eq assert
1✔
2546
            push  0 split push 0 eq assert push 0 eq assert
1✔
2547
            push  1 split push 1 eq assert push 0 eq assert
1✔
2548
            push  2 split push 2 eq assert push 0 eq assert
1✔
2549
            push 4294967297 split assert assert
1✔
2550
            halt
1✔
2551
        ))
1✔
2552
    }
1✔
2553

2554
    pub(crate) fn test_program_for_xx_add() -> ProgramAndInput {
1✔
2555
        ProgramAndInput::new(triton_program!(
1✔
2556
            push 5 push 6 push 7 push 8 push 9 push 10 xx_add halt
1✔
2557
        ))
1✔
2558
    }
1✔
2559

2560
    pub(crate) fn test_program_for_xx_mul() -> ProgramAndInput {
1✔
2561
        ProgramAndInput::new(triton_program!(
1✔
2562
            push 5 push 6 push 7 push 8 push 9 push 10 xx_mul halt
1✔
2563
        ))
1✔
2564
    }
1✔
2565

2566
    pub(crate) fn test_program_for_x_invert() -> ProgramAndInput {
1✔
2567
        ProgramAndInput::new(triton_program!(
1✔
2568
            push 5 push 6 push 7 x_invert halt
1✔
2569
        ))
1✔
2570
    }
1✔
2571

2572
    pub(crate) fn test_program_for_xb_mul() -> ProgramAndInput {
1✔
2573
        ProgramAndInput::new(triton_program!(
1✔
2574
            push 5 push 6 push 7 push 8 xb_mul halt
1✔
2575
        ))
1✔
2576
    }
1✔
2577

2578
    pub(crate) fn test_program_for_read_io_write_io() -> ProgramAndInput {
1✔
2579
        let program = triton_program!(
1✔
2580
            read_io 1 assert read_io 2 dup 1 dup 1 add write_io 1 mul push 5 write_io 2 halt
1✔
2581
        );
1✔
2582
        ProgramAndInput::new(program).with_input([1, 3, 14].map(|b| bfe!(b)))
3✔
2583
    }
1✔
2584

2585
    pub(crate) fn test_program_claim_in_ram_corresponds_to_currently_running_program(
1✔
2586
    ) -> ProgramAndInput {
1✔
2587
        let program = triton_program! {
1✔
2588
            dup 15 dup 15 dup 15 dup 15 dup 15  // _ [own_digest]
1✔
2589
            push 4 read_mem 5 pop 1             // _ [own_digest] [claim_digest]
1✔
2590
            assert_vector                       // _ [own_digest]
1✔
2591
            halt
1✔
2592
        };
1✔
2593

1✔
2594
        let program_digest = program.hash();
1✔
2595
        let enumerated_digest_elements = program_digest.values().into_iter().enumerate();
1✔
2596
        let initial_ram = enumerated_digest_elements
1✔
2597
            .map(|(address, d)| (bfe!(u64::try_from(address).unwrap()), d))
5✔
2598
            .collect::<HashMap<_, _>>();
1✔
2599
        let non_determinism = NonDeterminism::default().with_ram(initial_ram);
1✔
2600

1✔
2601
        ProgramAndInput::new(program).with_non_determinism(non_determinism)
1✔
2602
    }
1✔
2603

2604
    #[proptest]
512✔
2605
    fn xx_add(
2606
        #[strategy(arb())] left_operand: XFieldElement,
1✔
2607
        #[strategy(arb())] right_operand: XFieldElement,
1✔
2608
    ) {
2609
        let program = triton_program!(
2610
            push {right_operand.coefficients[2]}
2611
            push {right_operand.coefficients[1]}
2612
            push {right_operand.coefficients[0]}
2613
            push {left_operand.coefficients[2]}
2614
            push {left_operand.coefficients[1]}
2615
            push {left_operand.coefficients[0]}
2616
            xx_add
2617
            write_io 3
2618
            halt
2619
        );
2620
        let actual_stdout = VM::run(&program, [].into(), [].into())?;
2621
        let expected_stdout = (left_operand + right_operand).coefficients.to_vec();
2622
        prop_assert_eq!(expected_stdout, actual_stdout);
2623
    }
2624

2625
    #[proptest]
512✔
2626
    fn xx_mul(
2627
        #[strategy(arb())] left_operand: XFieldElement,
1✔
2628
        #[strategy(arb())] right_operand: XFieldElement,
1✔
2629
    ) {
2630
        let program = triton_program!(
2631
            push {right_operand.coefficients[2]}
2632
            push {right_operand.coefficients[1]}
2633
            push {right_operand.coefficients[0]}
2634
            push {left_operand.coefficients[2]}
2635
            push {left_operand.coefficients[1]}
2636
            push {left_operand.coefficients[0]}
2637
            xx_mul
2638
            write_io 3
2639
            halt
2640
        );
2641
        let actual_stdout = VM::run(&program, [].into(), [].into())?;
2642
        let expected_stdout = (left_operand * right_operand).coefficients.to_vec();
2643
        prop_assert_eq!(expected_stdout, actual_stdout);
2644
    }
2645

2646
    #[proptest]
256✔
2647
    fn xinv(
2648
        #[strategy(arb())]
2649
        #[filter(!#operand.is_zero())]
256✔
2650
        operand: XFieldElement,
1✔
2651
    ) {
2652
        let program = triton_program!(
2653
            push {operand.coefficients[2]}
2654
            push {operand.coefficients[1]}
2655
            push {operand.coefficients[0]}
2656
            x_invert
2657
            write_io 3
2658
            halt
2659
        );
2660
        let actual_stdout = VM::run(&program, [].into(), [].into())?;
2661
        let expected_stdout = operand.inverse().coefficients.to_vec();
2662
        prop_assert_eq!(expected_stdout, actual_stdout);
2663
    }
2664

2665
    #[proptest]
512✔
2666
    fn xb_mul(#[strategy(arb())] scalar: BFieldElement, #[strategy(arb())] operand: XFieldElement) {
1✔
2667
        let program = triton_program!(
2668
            push {operand.coefficients[2]}
2669
            push {operand.coefficients[1]}
2670
            push {operand.coefficients[0]}
2671
            push {scalar}
2672
            xb_mul
2673
            write_io 3
2674
            halt
2675
        );
2676
        let actual_stdout = VM::run(&program, [].into(), [].into())?;
2677
        let expected_stdout = (scalar * operand).coefficients.to_vec();
2678
        prop_assert_eq!(expected_stdout, actual_stdout);
2679
    }
2680

2681
    #[proptest]
512✔
2682
    fn pseudo_sub(
2683
        #[strategy(arb())] minuend: BFieldElement,
1✔
2684
        #[strategy(arb())] subtrahend: BFieldElement,
1✔
2685
    ) {
2686
        let program = triton_program!(
2687
            push {subtrahend} push {minuend} call sub write_io 1 halt
2688
            sub:
2689
                swap 1 push -1 mul add return
2690
        );
2691
        let actual_stdout = ProgramAndInput::new(program).run()?;
2692
        let expected_stdout = vec![minuend - subtrahend];
2693

2694
        prop_assert_eq!(expected_stdout, actual_stdout);
2695
    }
2696

2697
    // compile-time assertion
2698
    const _OP_STACK_IS_BIG_ENOUGH: () = std::assert!(2 * Digest::LEN <= OpStackElement::COUNT);
2699

2700
    #[test]
2701
    fn run_tvm_hello_world() {
1✔
2702
        let program = triton_program!(
1✔
2703
            push  10 // \n
1✔
2704
            push  33 // !
1✔
2705
            push 100 // d
1✔
2706
            push 108 // l
1✔
2707
            push 114 // r
1✔
2708
            push 111 // o
1✔
2709
            push  87 // W
1✔
2710
            push  32 //
1✔
2711
            push  44 // ,
1✔
2712
            push 111 // o
1✔
2713
            push 108 // l
1✔
2714
            push 108 // l
1✔
2715
            push 101 // e
1✔
2716
            push  72 // H
1✔
2717
            write_io 5 write_io 5 write_io 4
1✔
2718
            halt
1✔
2719
        );
1✔
2720
        let mut vm_state = VMState::new(&program, [].into(), [].into());
1✔
2721
        let_assert!(Ok(()) = vm_state.run());
1✔
2722
        assert!(BFieldElement::ZERO == vm_state.op_stack[ST0]);
1✔
2723
    }
1✔
2724

2725
    #[test]
2726
    fn run_tvm_merkle_step_right_sibling() {
1✔
2727
        let program_and_input = test_program_for_merkle_step_right_sibling();
1✔
2728
        let_assert!(Ok(_) = program_and_input.run());
1✔
2729
    }
1✔
2730

2731
    #[test]
2732
    fn run_tvm_merkle_step_left_sibling() {
1✔
2733
        let program_and_input = test_program_for_merkle_step_left_sibling();
1✔
2734
        let_assert!(Ok(_) = program_and_input.run());
1✔
2735
    }
1✔
2736

2737
    #[test]
2738
    fn run_tvm_merkle_step_mem_right_sibling() {
1✔
2739
        let program_and_input = test_program_for_merkle_step_mem_right_sibling();
1✔
2740
        let_assert!(Ok(_) = program_and_input.run());
1✔
2741
    }
1✔
2742

2743
    #[test]
2744
    fn run_tvm_merkle_step_mem_left_sibling() {
1✔
2745
        let program_and_input = test_program_for_merkle_step_mem_left_sibling();
1✔
2746
        let_assert!(Ok(_) = program_and_input.run());
1✔
2747
    }
1✔
2748

2749
    #[test]
2750
    fn run_tvm_halt_then_do_stuff() {
1✔
2751
        let program = triton_program!(halt push 1 push 2 add invert write_io 5);
1✔
2752
        let_assert!(Ok((aet, _)) = VM::trace_execution(&program, [].into(), [].into()));
1✔
2753

2754
        let_assert!(Some(last_processor_row) = aet.processor_trace.rows().into_iter().last());
1✔
2755
        let clk_count = last_processor_row[ProcessorMainColumn::CLK.main_index()];
1✔
2756
        assert!(BFieldElement::ZERO == clk_count);
1✔
2757

2758
        let last_instruction = last_processor_row[ProcessorMainColumn::CI.main_index()];
1✔
2759
        assert!(Instruction::Halt.opcode_b() == last_instruction);
1✔
2760

2761
        println!("{last_processor_row}");
1✔
2762
    }
1✔
2763

2764
    #[test]
2765
    fn run_tvm_basic_ram_read_write() {
1✔
2766
        let program = triton_program!(
1✔
2767
            push  8 push  5 write_mem 1 pop 1   // write  8 to address  5
1✔
2768
            push 18 push 15 write_mem 1 pop 1   // write 18 to address 15
1✔
2769
            push  5         read_mem  1 pop 2   // read from address  5
1✔
2770
            push 15         read_mem  1 pop 2   // read from address 15
1✔
2771
            push  7 push  5 write_mem 1 pop 1   // write  7 to address  5
1✔
2772
            push 15         read_mem  1         // _ 18 14
1✔
2773
            push  5         read_mem  1         // _ 18 14 7 4
1✔
2774
            halt
1✔
2775
        );
1✔
2776

1✔
2777
        let mut vm_state = VMState::new(&program, [].into(), [].into());
1✔
2778
        let_assert!(Ok(()) = vm_state.run());
1✔
2779
        assert!(4 == vm_state.op_stack[ST0].value());
1✔
2780
        assert!(7 == vm_state.op_stack[ST1].value());
1✔
2781
        assert!(14 == vm_state.op_stack[ST2].value());
1✔
2782
        assert!(18 == vm_state.op_stack[ST3].value());
1✔
2783
    }
1✔
2784

2785
    #[test]
2786
    fn run_tvm_edgy_ram_writes() {
1✔
2787
        let program = triton_program!(
1✔
2788
                        //       ┌ stack cannot shrink beyond this point
1✔
2789
                        //       ↓
1✔
2790
                        // _ 0 0 |
1✔
2791
            push 0      // _ 0 0 | 0
1✔
2792
            write_mem 1 // _ 0 1 |
1✔
2793
            push 5      // _ 0 1 | 5
1✔
2794
            swap 1      // _ 0 5 | 1
1✔
2795
            push 3      // _ 0 5 | 1 3
1✔
2796
            swap 1      // _ 0 5 | 3 1
1✔
2797
            pop 1       // _ 0 5 | 3
1✔
2798
            write_mem 1 // _ 0 4 |
1✔
2799
            push -1 add // _ 0 3 |
1✔
2800
            read_mem 1  // _ 0 5 | 2
1✔
2801
            swap 2      // _ 2 5 | 0
1✔
2802
            pop 1       // _ 2 5 |
1✔
2803
            swap 1      // _ 5 2 |
1✔
2804
            push 1      // _ 5 2 | 1
1✔
2805
            add         // _ 5 3 |
1✔
2806
            read_mem 1  // _ 5 5 | 2
1✔
2807
            halt
1✔
2808
        );
1✔
2809

1✔
2810
        let mut vm_state = VMState::new(&program, [].into(), [].into());
1✔
2811
        let_assert!(Ok(()) = vm_state.run());
1✔
2812
        assert!(2_u64 == vm_state.op_stack[ST0].value());
1✔
2813
        assert!(5_u64 == vm_state.op_stack[ST1].value());
1✔
2814
        assert!(5_u64 == vm_state.op_stack[ST2].value());
1✔
2815
    }
1✔
2816

2817
    #[proptest]
256✔
2818
    fn triton_assembly_merkle_tree_authentication_path_verification(
2819
        leaved_merkle_tree: LeavedMerkleTreeTestData,
2820
    ) {
2821
        let merkle_tree = &leaved_merkle_tree.merkle_tree;
2822
        let auth_path = |&i| merkle_tree.authentication_structure(&[i]).unwrap();
30,723✔
2823

2824
        let revealed_indices = &leaved_merkle_tree.revealed_indices;
2825
        let num_authentication_paths = revealed_indices.len();
2826

2827
        let auth_paths = revealed_indices.iter().flat_map(auth_path).collect_vec();
2828
        let non_determinism = NonDeterminism::default().with_digests(auth_paths);
2829

2830
        let mut public_input = vec![(num_authentication_paths as u64).into()];
2831
        public_input.extend(leaved_merkle_tree.root().reversed().values());
2832
        for &leaf_index in revealed_indices {
2833
            let node_index = (leaf_index + leaved_merkle_tree.num_leaves()) as u64;
2834
            public_input.push(node_index.into());
2835
            let revealed_leaf = leaved_merkle_tree.leaves_as_digests[leaf_index];
2836
            public_input.extend(revealed_leaf.reversed().values());
2837
        }
2838

2839
        let program = MERKLE_TREE_AUTHENTICATION_PATH_VERIFY.clone();
2840
        assert!(let Ok(_) = VM::run(&program, public_input.into(), non_determinism));
2841
    }
2842

2843
    #[proptest]
256✔
2844
    fn merkle_tree_updating_program_correctly_updates_a_merkle_tree(
2845
        program_for_merkle_tree_update: ProgramForMerkleTreeUpdate,
2846
    ) {
2847
        prop_assert!(program_for_merkle_tree_update.is_integral());
2848
    }
2849

2850
    #[proptest(cases = 10)]
20✔
2851
    fn prove_verify_merkle_tree_update(
2852
        program_for_merkle_tree_update: ProgramForMerkleTreeUpdate,
2853
        #[strategy(1_usize..=4)] log_2_fri_expansion_factor: usize,
1✔
2854
    ) {
2855
        prove_and_verify(
2856
            program_for_merkle_tree_update.assemble(),
2857
            log_2_fri_expansion_factor,
2858
        );
2859
    }
2860

2861
    #[proptest]
768✔
2862
    fn run_tvm_get_collinear_y(
2863
        #[strategy(arb())] p0: (BFieldElement, BFieldElement),
1✔
2864
        #[strategy(arb())]
2865
        #[filter(#p0.0 != #p1.0)]
256✔
2866
        p1: (BFieldElement, BFieldElement),
1✔
2867
        #[strategy(arb())] p2_x: BFieldElement,
1✔
2868
    ) {
2869
        let p2_y = Polynomial::get_colinear_y(p0, p1, p2_x);
2870

2871
        let get_collinear_y_program = triton_program!(
2872
            read_io 5                       // p0 p1 p2_x
2873
            swap 3 push -1 mul dup 1 add    // dy = p0_y - p1_y
2874
            dup 3 push -1 mul dup 5 add mul // dy·(p2_x - p0_x)
2875
            dup 3 dup 3 push -1 mul add     // dx = p0_x - p1_x
2876
            invert mul add                  // compute result
2877
            swap 3 pop 3                    // leave a clean stack
2878
            write_io 1 halt
2879
        );
2880

2881
        let public_input = vec![p2_x, p1.1, p1.0, p0.1, p0.0];
2882
        let_assert!(Ok(output) = VM::run(&get_collinear_y_program, public_input.into(), [].into()));
2883
        prop_assert_eq!(p2_y, output[0]);
2884
    }
2885

2886
    #[test]
2887
    fn run_tvm_countdown_from_10() {
1✔
2888
        let countdown_program = triton_program!(
1✔
2889
            push 10
1✔
2890
            call loop
1✔
2891

1✔
2892
            loop:
1✔
2893
                dup 0
1✔
2894
                write_io 1
1✔
2895
                push -1
1✔
2896
                add
1✔
2897
                dup 0
1✔
2898
                skiz
1✔
2899
                  recurse
1✔
2900
                write_io 1
1✔
2901
                halt
1✔
2902
        );
1✔
2903

1✔
2904
        let_assert!(Ok(standard_out) = VM::run(&countdown_program, [].into(), [].into()));
1✔
2905
        let expected = (0..=10).map(BFieldElement::new).rev().collect_vec();
1✔
2906
        assert!(expected == standard_out);
1✔
2907
    }
1✔
2908

2909
    #[test]
2910
    fn run_tvm_fibonacci() {
1✔
2911
        for (input, expected_output) in [(0, 1), (7, 21), (11, 144)] {
3✔
2912
            let program_and_input =
3✔
2913
                ProgramAndInput::new(FIBONACCI_SEQUENCE.clone()).with_input(bfe_array![input]);
3✔
2914
            let_assert!(Ok(output) = program_and_input.run());
3✔
2915
            let_assert!(&[output] = &output[..]);
3✔
2916
            assert!(expected_output == output.value(), "input was: {input}");
3✔
2917
        }
2918
    }
1✔
2919

2920
    #[test]
2921
    fn run_tvm_swap() {
1✔
2922
        let program = triton_program!(push 1 push 2 swap 1 assert write_io 1 halt);
1✔
2923
        let_assert!(Ok(standard_out) = VM::run(&program, [].into(), [].into()));
1✔
2924
        assert!(bfe!(2) == standard_out[0]);
1✔
2925
    }
1✔
2926

2927
    #[test]
2928
    fn swap_st0_is_like_no_op() {
1✔
2929
        let program = triton_program!(push 42 swap 0 write_io 1 halt);
1✔
2930
        let_assert!(Ok(standard_out) = VM::run(&program, [].into(), [].into()));
1✔
2931
        assert!(bfe!(42) == standard_out[0]);
1✔
2932
    }
1✔
2933

2934
    #[test]
2935
    fn read_mem_uninitialized() {
1✔
2936
        let program = triton_program!(read_mem 3 halt);
1✔
2937
        let_assert!(Ok((aet, _)) = VM::trace_execution(&program, [].into(), [].into()));
1✔
2938
        assert!(2 == aet.processor_trace.nrows());
1✔
2939
    }
1✔
2940

2941
    #[test]
2942
    fn read_non_deterministically_initialized_ram_at_address_0() {
1✔
2943
        let program = triton_program!(push 0 read_mem 1 pop 1 write_io 1 halt);
1✔
2944

1✔
2945
        let mut initial_ram = HashMap::new();
1✔
2946
        initial_ram.insert(bfe!(0), bfe!(42));
1✔
2947
        let non_determinism = NonDeterminism::default().with_ram(initial_ram);
1✔
2948
        let program_and_input = ProgramAndInput::new(program).with_non_determinism(non_determinism);
1✔
2949

1✔
2950
        let_assert!(Ok(public_output) = program_and_input.run());
1✔
2951
        let_assert!(&[output] = &public_output[..]);
1✔
2952
        assert!(42 == output.value());
1✔
2953

2954
        prove_and_verify(
1✔
2955
            program_and_input,
1✔
2956
            DEFAULT_LOG2_FRI_EXPANSION_FACTOR_FOR_TESTS,
1✔
2957
        );
1✔
2958
    }
1✔
2959

2960
    #[proptest(cases = 10)]
30✔
2961
    fn read_non_deterministically_initialized_ram_at_random_address(
2962
        #[strategy(arb())] uninitialized_address: BFieldElement,
1✔
2963
        #[strategy(arb())]
2964
        #[filter(#uninitialized_address != #initialized_address)]
10✔
2965
        initialized_address: BFieldElement,
1✔
2966
        #[strategy(arb())] value: BFieldElement,
1✔
2967
    ) {
2968
        let program = triton_program!(
2969
            push {uninitialized_address} read_mem 1 pop 1 write_io 1
2970
            push {initialized_address} read_mem 1 pop 1 write_io 1
2971
            halt
2972
        );
2973

2974
        let mut initial_ram = HashMap::new();
2975
        initial_ram.insert(initialized_address, value);
2976
        let non_determinism = NonDeterminism::default().with_ram(initial_ram);
2977
        let program_and_input = ProgramAndInput::new(program).with_non_determinism(non_determinism);
2978

2979
        let_assert!(Ok(public_output) = program_and_input.run());
2980
        let_assert!(&[uninit_value, init_value] = &public_output[..]);
2981
        assert!(0 == uninit_value.value());
2982
        assert!(value == init_value);
2983

2984
        prove_and_verify(
2985
            program_and_input,
2986
            DEFAULT_LOG2_FRI_EXPANSION_FACTOR_FOR_TESTS,
2987
        );
2988
    }
2989

2990
    #[test]
2991
    fn program_without_halt() {
1✔
2992
        let program = triton_program!(nop);
1✔
2993
        let_assert!(Err(err) = VM::trace_execution(&program, [].into(), [].into()));
1✔
2994
        let_assert!(InstructionError::InstructionPointerOverflow = err.source);
1✔
2995
    }
1✔
2996

2997
    #[test]
2998
    fn verify_sudoku() {
1✔
2999
        let program = VERIFY_SUDOKU.clone();
1✔
3000
        let sudoku = [
1✔
3001
            8, 5, 9, /**/ 7, 6, 1, /**/ 4, 2, 3, //
1✔
3002
            4, 2, 6, /**/ 8, 5, 3, /**/ 7, 9, 1, //
1✔
3003
            7, 1, 3, /**/ 9, 2, 4, /**/ 8, 5, 6, //
1✔
3004
            /*************************************/
1✔
3005
            9, 6, 1, /**/ 5, 3, 7, /**/ 2, 8, 4, //
1✔
3006
            2, 8, 7, /**/ 4, 1, 9, /**/ 6, 3, 5, //
1✔
3007
            3, 4, 5, /**/ 2, 8, 6, /**/ 1, 7, 9, //
1✔
3008
            /*************************************/
1✔
3009
            5, 3, 4, /**/ 6, 7, 8, /**/ 9, 1, 2, //
1✔
3010
            6, 7, 2, /**/ 1, 9, 5, /**/ 3, 4, 8, //
1✔
3011
            1, 9, 8, /**/ 3, 4, 2, /**/ 5, 6, 7, //
1✔
3012
        ];
1✔
3013

1✔
3014
        let std_in = PublicInput::from(sudoku.map(|b| bfe!(b)));
81✔
3015
        let secret_in = NonDeterminism::default();
1✔
3016
        assert!(let Ok(_) = VM::trace_execution(&program, std_in, secret_in));
1✔
3017

3018
        // rows and columns adhere to Sudoku rules, boxes do not
3019
        let bad_sudoku = [
1✔
3020
            1, 2, 3, /**/ 4, 5, 7, /**/ 8, 9, 6, //
1✔
3021
            4, 3, 1, /**/ 5, 2, 9, /**/ 6, 7, 8, //
1✔
3022
            2, 7, 9, /**/ 6, 1, 3, /**/ 5, 8, 4, //
1✔
3023
            /*************************************/
1✔
3024
            7, 6, 5, /**/ 3, 4, 8, /**/ 9, 2, 1, //
1✔
3025
            5, 1, 4, /**/ 9, 8, 6, /**/ 7, 3, 2, //
1✔
3026
            6, 8, 2, /**/ 7, 9, 4, /**/ 1, 5, 3, //
1✔
3027
            /*************************************/
1✔
3028
            3, 5, 6, /**/ 8, 7, 2, /**/ 4, 1, 9, //
1✔
3029
            9, 4, 8, /**/ 1, 3, 5, /**/ 2, 6, 7, //
1✔
3030
            8, 9, 7, /**/ 2, 6, 1, /**/ 3, 4, 5, //
1✔
3031
        ];
1✔
3032
        let bad_std_in = PublicInput::from(bad_sudoku.map(|b| bfe!(b)));
81✔
3033
        let secret_in = NonDeterminism::default();
1✔
3034
        let_assert!(Err(err) = VM::trace_execution(&program, bad_std_in, secret_in));
1✔
3035
        let_assert!(InstructionError::AssertionFailed = err.source);
1✔
3036
    }
1✔
3037

3038
    fn instruction_does_not_change_vm_state_when_crashing_vm(
23✔
3039
        program_and_input: ProgramAndInput,
23✔
3040
        num_preparatory_steps: usize,
23✔
3041
    ) {
23✔
3042
        let mut vm_state = VMState::new(
23✔
3043
            &program_and_input.program,
23✔
3044
            program_and_input.public_input,
23✔
3045
            program_and_input.non_determinism,
23✔
3046
        );
23✔
3047
        for i in 0..num_preparatory_steps {
37✔
3048
            assert!(let Ok(_) = vm_state.step(), "failed during step {i}");
37✔
3049
        }
3050
        let pre_crash_state = vm_state.clone();
23✔
3051
        assert!(let Err(_) = vm_state.step());
23✔
3052
        assert!(pre_crash_state == vm_state);
23✔
3053
    }
23✔
3054

3055
    #[test]
3056
    fn instruction_pop_does_not_change_vm_state_when_crashing_vm() {
1✔
3057
        let program = triton_program! { push 0 pop 2 halt };
1✔
3058
        instruction_does_not_change_vm_state_when_crashing_vm(ProgramAndInput::new(program), 1);
1✔
3059
    }
1✔
3060

3061
    #[test]
3062
    fn instruction_divine_does_not_change_vm_state_when_crashing_vm() {
1✔
3063
        let program = triton_program! { divine 1 halt };
1✔
3064
        instruction_does_not_change_vm_state_when_crashing_vm(ProgramAndInput::new(program), 0);
1✔
3065
    }
1✔
3066

3067
    #[test]
3068
    fn instruction_assert_does_not_change_vm_state_when_crashing_vm() {
1✔
3069
        let program = triton_program! { push 0 assert halt };
1✔
3070
        instruction_does_not_change_vm_state_when_crashing_vm(ProgramAndInput::new(program), 1);
1✔
3071
    }
1✔
3072

3073
    #[test]
3074
    fn instruction_merkle_step_does_not_change_vm_state_when_crashing_vm_invalid_node_index() {
1✔
3075
        let non_u32 = u64::from(u32::MAX) + 1;
1✔
3076
        let program = triton_program! { push {non_u32} swap 5 merkle_step halt };
1✔
3077
        let nondeterminism = NonDeterminism::default().with_digests([Digest::default()]);
1✔
3078
        let program_and_input = ProgramAndInput::new(program).with_non_determinism(nondeterminism);
1✔
3079
        instruction_does_not_change_vm_state_when_crashing_vm(program_and_input, 2);
1✔
3080
    }
1✔
3081

3082
    #[test]
3083
    fn instruction_merkle_step_does_not_change_vm_state_when_crashing_vm_no_nd_digests() {
1✔
3084
        let valid_u32 = u64::from(u32::MAX);
1✔
3085
        let program = triton_program! { push {valid_u32} swap 5 merkle_step halt };
1✔
3086
        let program_and_input = ProgramAndInput::new(program);
1✔
3087
        instruction_does_not_change_vm_state_when_crashing_vm(program_and_input, 2);
1✔
3088
    }
1✔
3089

3090
    #[test]
3091
    fn instruction_merkle_step_mem_does_not_change_vm_state_when_crashing_vm_invalid_node_index() {
1✔
3092
        let non_u32 = u64::from(u32::MAX) + 1;
1✔
3093
        let program = triton_program! { push {non_u32} swap 5 merkle_step_mem halt };
1✔
3094
        let program_and_input = ProgramAndInput::new(program);
1✔
3095
        instruction_does_not_change_vm_state_when_crashing_vm(program_and_input, 2);
1✔
3096
    }
1✔
3097

3098
    #[test]
3099
    fn instruction_assert_vector_does_not_change_vm_state_when_crashing_vm() {
1✔
3100
        let program = triton_program! { push 0 push 1 push 0 push 0 push 0 assert_vector halt };
1✔
3101
        instruction_does_not_change_vm_state_when_crashing_vm(ProgramAndInput::new(program), 5);
1✔
3102
    }
1✔
3103

3104
    #[test]
3105
    fn instruction_sponge_absorb_does_not_change_vm_state_when_crashing_vm_sponge_uninit() {
1✔
3106
        let ten_pushes = triton_asm![push 0; 10];
1✔
3107
        let program = triton_program! { {&ten_pushes} sponge_absorb halt };
1✔
3108
        instruction_does_not_change_vm_state_when_crashing_vm(ProgramAndInput::new(program), 10);
1✔
3109
    }
1✔
3110

3111
    #[test]
3112
    fn instruction_sponge_absorb_does_not_change_vm_state_when_crashing_vm_stack_too_small() {
1✔
3113
        let program = triton_program! { sponge_init sponge_absorb halt };
1✔
3114
        instruction_does_not_change_vm_state_when_crashing_vm(ProgramAndInput::new(program), 1);
1✔
3115
    }
1✔
3116

3117
    #[test]
3118
    fn instruction_sponge_absorb_mem_does_not_change_vm_state_when_crashing_vm_sponge_uninit() {
1✔
3119
        let program = triton_program! { sponge_absorb_mem halt };
1✔
3120
        instruction_does_not_change_vm_state_when_crashing_vm(ProgramAndInput::new(program), 0);
1✔
3121
    }
1✔
3122

3123
    #[test]
3124
    fn instruction_sponge_squeeze_does_not_change_vm_state_when_crashing_vm() {
1✔
3125
        let program = triton_program! { sponge_squeeze halt };
1✔
3126
        instruction_does_not_change_vm_state_when_crashing_vm(ProgramAndInput::new(program), 0);
1✔
3127
    }
1✔
3128

3129
    #[test]
3130
    fn instruction_invert_does_not_change_vm_state_when_crashing_vm() {
1✔
3131
        let program = triton_program! { push 0 invert halt };
1✔
3132
        instruction_does_not_change_vm_state_when_crashing_vm(ProgramAndInput::new(program), 1);
1✔
3133
    }
1✔
3134

3135
    #[test]
3136
    fn instruction_lt_does_not_change_vm_state_when_crashing_vm() {
1✔
3137
        let non_u32 = u64::from(u32::MAX) + 1;
1✔
3138
        let program = triton_program! { push {non_u32} lt halt };
1✔
3139
        instruction_does_not_change_vm_state_when_crashing_vm(ProgramAndInput::new(program), 1);
1✔
3140
    }
1✔
3141

3142
    #[test]
3143
    fn instruction_and_does_not_change_vm_state_when_crashing_vm() {
1✔
3144
        let non_u32 = u64::from(u32::MAX) + 1;
1✔
3145
        let program = triton_program! { push {non_u32} and halt };
1✔
3146
        instruction_does_not_change_vm_state_when_crashing_vm(ProgramAndInput::new(program), 1);
1✔
3147
    }
1✔
3148

3149
    #[test]
3150
    fn instruction_xor_does_not_change_vm_state_when_crashing_vm() {
1✔
3151
        let non_u32 = u64::from(u32::MAX) + 1;
1✔
3152
        let program = triton_program! { push {non_u32} xor halt };
1✔
3153
        instruction_does_not_change_vm_state_when_crashing_vm(ProgramAndInput::new(program), 1);
1✔
3154
    }
1✔
3155

3156
    #[test]
3157
    fn instruction_log_2_floor_on_non_u32_operand_does_not_change_vm_state_when_crashing_vm() {
1✔
3158
        let non_u32 = u64::from(u32::MAX) + 1;
1✔
3159
        let program = triton_program! { push {non_u32} log_2_floor halt };
1✔
3160
        instruction_does_not_change_vm_state_when_crashing_vm(ProgramAndInput::new(program), 1);
1✔
3161
    }
1✔
3162

3163
    #[test]
3164
    fn instruction_log_2_floor_on_operand_0_does_not_change_vm_state_when_crashing_vm() {
1✔
3165
        let program = triton_program! { push 0 log_2_floor halt };
1✔
3166
        instruction_does_not_change_vm_state_when_crashing_vm(ProgramAndInput::new(program), 1);
1✔
3167
    }
1✔
3168

3169
    #[test]
3170
    fn instruction_pow_does_not_change_vm_state_when_crashing_vm() {
1✔
3171
        let non_u32 = u64::from(u32::MAX) + 1;
1✔
3172
        let program = triton_program! { push {non_u32} push 0 pow halt };
1✔
3173
        instruction_does_not_change_vm_state_when_crashing_vm(ProgramAndInput::new(program), 2);
1✔
3174
    }
1✔
3175

3176
    #[test]
3177
    fn instruction_div_mod_on_non_u32_operand_does_not_change_vm_state_when_crashing_vm() {
1✔
3178
        let non_u32 = u64::from(u32::MAX) + 1;
1✔
3179
        let program = triton_program! { push {non_u32} push 0 div_mod halt };
1✔
3180
        instruction_does_not_change_vm_state_when_crashing_vm(ProgramAndInput::new(program), 2);
1✔
3181
    }
1✔
3182

3183
    #[test]
3184
    fn instruction_div_mod_on_denominator_0_does_not_change_vm_state_when_crashing_vm() {
1✔
3185
        let program = triton_program! { push 0 push 1 div_mod halt };
1✔
3186
        instruction_does_not_change_vm_state_when_crashing_vm(ProgramAndInput::new(program), 2);
1✔
3187
    }
1✔
3188

3189
    #[test]
3190
    fn instruction_pop_count_does_not_change_vm_state_when_crashing_vm() {
1✔
3191
        let non_u32 = u64::from(u32::MAX) + 1;
1✔
3192
        let program = triton_program! { push {non_u32} pop_count halt };
1✔
3193
        instruction_does_not_change_vm_state_when_crashing_vm(ProgramAndInput::new(program), 1);
1✔
3194
    }
1✔
3195

3196
    #[test]
3197
    fn instruction_x_invert_does_not_change_vm_state_when_crashing_vm() {
1✔
3198
        let program = triton_program! { x_invert halt };
1✔
3199
        instruction_does_not_change_vm_state_when_crashing_vm(ProgramAndInput::new(program), 0);
1✔
3200
    }
1✔
3201

3202
    #[test]
3203
    fn instruction_read_io_does_not_change_vm_state_when_crashing_vm() {
1✔
3204
        let program = triton_program! { read_io 1 halt };
1✔
3205
        instruction_does_not_change_vm_state_when_crashing_vm(ProgramAndInput::new(program), 0);
1✔
3206
    }
1✔
3207

3208
    #[proptest]
256✔
3209
    fn serialize_deserialize_vm_state_to_and_from_json_is_identity(
3210
        #[strategy(arb())] vm_state: VMState,
1✔
3211
    ) {
3212
        let serialized = serde_json::to_string(&vm_state).unwrap();
3213
        let deserialized = serde_json::from_str(&serialized).unwrap();
3214
        prop_assert_eq!(vm_state, deserialized);
3215
    }
3216

3217
    #[proptest]
768✔
3218
    fn xx_dot_step(
3219
        #[strategy(0_usize..=25)] n: usize,
1✔
3220
        #[strategy(vec(arb(), #n))] lhs: Vec<XFieldElement>,
256✔
3221
        #[strategy(vec(arb(), #n))] rhs: Vec<XFieldElement>,
256✔
3222
        #[strategy(arb())] lhs_address: BFieldElement,
1✔
3223
        #[strategy(arb())] rhs_address: BFieldElement,
1✔
3224
    ) {
3225
        let mem_region_size = (n * EXTENSION_DEGREE) as u64;
3226
        let mem_region = |addr: BFieldElement| addr.value()..addr.value() + mem_region_size;
512✔
3227
        let right_in_left = mem_region(lhs_address).contains(&rhs_address.value());
3228
        let left_in_right = mem_region(rhs_address).contains(&lhs_address.value());
3229
        if right_in_left || left_in_right {
3230
            let reason = "storing into overlapping regions would overwrite";
3231
            return Err(TestCaseError::Reject(reason.into()));
3232
        }
3233

3234
        let lhs_flat = lhs.iter().flat_map(|&xfe| xfe.coefficients).collect_vec();
3,238✔
3235
        let rhs_flat = rhs.iter().flat_map(|&xfe| xfe.coefficients).collect_vec();
3,238✔
3236
        let mut ram = HashMap::new();
3237
        for (i, &l, &r) in izip!(0.., &lhs_flat, &rhs_flat) {
3238
            ram.insert(lhs_address + bfe!(i), l);
3239
            ram.insert(rhs_address + bfe!(i), r);
3240
        }
3241

3242
        let public_input = PublicInput::default();
3243
        let secret_input = NonDeterminism::default().with_ram(ram);
3244
        let many_xx_dot_steps = triton_asm![xx_dot_step; n];
3245
        let program = triton_program! {
3246
            push 0 push 0 push 0
3247

3248
            push {lhs_address}
3249
            push {rhs_address}
3250

3251
            {&many_xx_dot_steps}
3252
            halt
3253
        };
3254

3255
        let mut vmstate = VMState::new(&program, public_input, secret_input);
3256
        prop_assert!(vmstate.run().is_ok());
3257

3258
        prop_assert_eq!(
3259
            vmstate.op_stack.pop().unwrap(),
3260
            rhs_address + bfe!(rhs_flat.len() as u64)
3261
        );
3262
        prop_assert_eq!(
3263
            vmstate.op_stack.pop().unwrap(),
3264
            lhs_address + bfe!(lhs_flat.len() as u64)
3265
        );
3266

3267
        let observed_dot_product = vmstate.op_stack.pop_extension_field_element().unwrap();
3268
        let expected_dot_product = lhs
3269
            .into_iter()
3270
            .zip(rhs)
3271
            .map(|(l, r)| l * r)
3,238✔
3272
            .sum::<XFieldElement>();
3273
        prop_assert_eq!(expected_dot_product, observed_dot_product);
3274
    }
3275

3276
    #[proptest]
768✔
3277
    fn xb_dot_step(
3278
        #[strategy(0_usize..=25)] n: usize,
1✔
3279
        #[strategy(vec(arb(), #n))] lhs: Vec<XFieldElement>,
256✔
3280
        #[strategy(vec(arb(), #n))] rhs: Vec<BFieldElement>,
256✔
3281
        #[strategy(arb())] lhs_address: BFieldElement,
1✔
3282
        #[strategy(arb())] rhs_address: BFieldElement,
1✔
3283
    ) {
3284
        let mem_region_size_lhs = (n * EXTENSION_DEGREE) as u64;
3285
        let mem_region_lhs = lhs_address.value()..lhs_address.value() + mem_region_size_lhs;
3286
        let mem_region_rhs = rhs_address.value()..rhs_address.value() + n as u64;
3287
        let right_in_left = mem_region_lhs.contains(&rhs_address.value());
3288
        let left_in_right = mem_region_rhs.contains(&lhs_address.value());
3289
        if right_in_left || left_in_right {
3290
            let reason = "storing into overlapping regions would overwrite";
3291
            return Err(TestCaseError::Reject(reason.into()));
3292
        }
3293

3294
        let lhs_flat = lhs.iter().flat_map(|&xfe| xfe.coefficients).collect_vec();
3,302✔
3295
        let mut ram = HashMap::new();
3296
        for (i, &l) in (0..).zip(&lhs_flat) {
3297
            ram.insert(lhs_address + bfe!(i), l);
3298
        }
3299
        for (i, &r) in (0..).zip(&rhs) {
3300
            ram.insert(rhs_address + bfe!(i), r);
3301
        }
3302

3303
        let public_input = PublicInput::default();
3304
        let secret_input = NonDeterminism::default().with_ram(ram);
3305
        let many_xb_dot_steps = triton_asm![xb_dot_step; n];
3306
        let program = triton_program! {
3307
            push 0 push 0 push 0
3308

3309
            push {lhs_address}
3310
            push {rhs_address}
3311

3312
            {&many_xb_dot_steps}
3313
            halt
3314
        };
3315

3316
        let mut vmstate = VMState::new(&program, public_input, secret_input);
3317
        prop_assert!(vmstate.run().is_ok());
3318

3319
        prop_assert_eq!(
3320
            vmstate.op_stack.pop().unwrap(),
3321
            rhs_address + bfe!(rhs.len() as u64)
3322
        );
3323
        prop_assert_eq!(
3324
            vmstate.op_stack.pop().unwrap(),
3325
            lhs_address + bfe!(lhs_flat.len() as u64)
3326
        );
3327
        let observed_dot_product = vmstate.op_stack.pop_extension_field_element().unwrap();
3328
        let expected_dot_product = lhs
3329
            .into_iter()
3330
            .zip(rhs)
3331
            .map(|(l, r)| l * r)
3,302✔
3332
            .sum::<XFieldElement>();
3333
        prop_assert_eq!(expected_dot_product, observed_dot_product);
3334
    }
3335
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc