1use miden_air::{
2 Felt, FieldElement, RowIndex,
3 trace::{chiplets::hasher::HasherState, decoder::NUM_USER_OP_HELPERS},
4};
5use miden_core::{
6 QuadFelt, WORD_SIZE, Word, ZERO,
7 crypto::{hash::Rpo256, merkle::MerklePath},
8 precompile::{PrecompileTranscript, PrecompileTranscriptState},
9};
10
11use crate::{
12 AdviceProvider, ContextId, ErrorContext, ExecutionError, ProcessState,
13 chiplets::{CircuitEvaluation, MAX_NUM_ACE_WIRES, PTR_OFFSET_ELEM, PTR_OFFSET_WORD},
14 errors::AceError,
15 fast::{FastProcessor, STACK_BUFFER_SIZE, Tracer, memory::Memory},
16 processor::{
17 HasherInterface, MemoryInterface, OperationHelperRegisters, Processor, StackInterface,
18 SystemInterface,
19 },
20};
21
22impl Processor for FastProcessor {
23 type HelperRegisters = NoopHelperRegisters;
24 type System = Self;
25 type Stack = Self;
26 type AdviceProvider = AdviceProvider;
27 type Memory = Memory;
28 type Hasher = Self;
29
30 #[inline(always)]
31 fn stack(&mut self) -> &mut Self::Stack {
32 self
33 }
34
35 #[inline(always)]
36 fn state(&mut self) -> ProcessState<'_> {
37 self.state()
38 }
39
40 #[inline(always)]
41 fn advice_provider(&mut self) -> &mut Self::AdviceProvider {
42 &mut self.advice
43 }
44
45 #[inline(always)]
46 fn memory(&mut self) -> &mut Self::Memory {
47 &mut self.memory
48 }
49
50 #[inline(always)]
51 fn hasher(&mut self) -> &mut Self::Hasher {
52 self
53 }
54
55 #[inline(always)]
56 fn system(&mut self) -> &mut Self::System {
57 self
58 }
59
60 #[inline(always)]
61 fn precompile_transcript_state(&self) -> PrecompileTranscriptState {
62 self.pc_transcript.state()
63 }
64
65 #[inline(always)]
66 fn set_precompile_transcript_state(&mut self, state: PrecompileTranscriptState) {
67 self.pc_transcript = PrecompileTranscript::from_state(state);
68 }
69
70 fn op_eval_circuit(
93 &mut self,
94 err_ctx: &impl ErrorContext,
95 tracer: &mut impl Tracer,
96 ) -> Result<(), ExecutionError> {
97 let num_eval = self.stack_get(2);
98 let num_read = self.stack_get(1);
99 let ptr = self.stack_get(0);
100 let ctx = self.ctx;
101 let circuit_evaluation = eval_circuit_fast_(
102 ctx,
103 ptr,
104 self.clk,
105 num_read,
106 num_eval,
107 &mut self.memory,
108 err_ctx,
109 tracer,
110 )?;
111 self.ace.add_circuit_evaluation(self.clk, circuit_evaluation.clone());
112 tracer.record_circuit_evaluation(self.clk, circuit_evaluation);
113
114 Ok(())
115 }
116}
117
118impl HasherInterface for FastProcessor {
119 #[inline(always)]
120 fn permute(&mut self, mut input_state: HasherState) -> (Felt, HasherState) {
121 Rpo256::apply_permutation(&mut input_state);
122
123 (ZERO, input_state)
125 }
126
127 #[inline(always)]
128 fn verify_merkle_root(
129 &mut self,
130 claimed_root: Word,
131 value: Word,
132 path: Option<&MerklePath>,
133 index: Felt,
134 on_err: impl FnOnce() -> ExecutionError,
135 ) -> Result<Felt, ExecutionError> {
136 let path = path.expect("fast processor expects a valid Merkle path");
137 match path.verify(index.as_int(), value, &claimed_root) {
138 Ok(_) => Ok(ZERO),
140 Err(_) => Err(on_err()),
141 }
142 }
143
144 #[inline(always)]
145 fn update_merkle_root(
146 &mut self,
147 claimed_old_root: Word,
148 old_value: Word,
149 new_value: Word,
150 path: Option<&MerklePath>,
151 index: Felt,
152 on_err: impl FnOnce() -> ExecutionError,
153 ) -> Result<(Felt, Word), ExecutionError> {
154 let path = path.expect("fast processor expects a valid Merkle path");
155
156 if path.verify(index.as_int(), old_value, &claimed_old_root).is_err() {
158 return Err(on_err());
159 };
160
161 let new_root = path.compute_root(index.as_int(), new_value).map_err(|_| on_err())?;
163
164 Ok((ZERO, new_root))
165 }
166}
167
168impl SystemInterface for FastProcessor {
169 #[inline(always)]
170 fn caller_hash(&self) -> Word {
171 self.caller_hash
172 }
173
174 #[inline(always)]
175 fn clk(&self) -> RowIndex {
176 self.clk
177 }
178
179 #[inline(always)]
180 fn ctx(&self) -> ContextId {
181 self.ctx
182 }
183}
184
185impl StackInterface for FastProcessor {
186 #[inline(always)]
187 fn top(&self) -> &[Felt] {
188 self.stack_top()
189 }
190
191 #[inline(always)]
192 fn top_mut(&mut self) -> &mut [Felt] {
193 self.stack_top_mut()
194 }
195
196 #[inline(always)]
197 fn get(&self, idx: usize) -> Felt {
198 self.stack_get(idx)
199 }
200
201 #[inline(always)]
202 fn get_mut(&mut self, idx: usize) -> &mut Felt {
203 self.stack_get_mut(idx)
204 }
205
206 #[inline(always)]
207 fn get_word(&self, start_idx: usize) -> Word {
208 self.stack_get_word(start_idx)
209 }
210
211 #[inline(always)]
212 fn depth(&self) -> u32 {
213 self.stack_depth()
214 }
215
216 #[inline(always)]
217 fn set(&mut self, idx: usize, element: Felt) {
218 self.stack_write(idx, element)
219 }
220
221 #[inline(always)]
222 fn set_word(&mut self, start_idx: usize, word: &Word) {
223 self.stack_write_word(start_idx, word);
224 }
225
226 #[inline(always)]
227 fn swap(&mut self, idx1: usize, idx2: usize) {
228 self.stack_swap(idx1, idx2)
229 }
230
231 #[inline(always)]
232 fn swapw_nth(&mut self, n: usize) {
233 let (rest_of_stack, top_word) = self.stack.split_at_mut(self.stack_top_idx - WORD_SIZE);
239 let (_, nth_word) = rest_of_stack.split_at_mut(rest_of_stack.len() - n * WORD_SIZE);
240
241 nth_word[0..WORD_SIZE].swap_with_slice(&mut top_word[0..WORD_SIZE]);
242 }
243
244 #[inline(always)]
245 fn rotate_left(&mut self, n: usize) {
246 let rotation_bot_index = self.stack_top_idx - n;
247 let new_stack_top_element = self.stack[rotation_bot_index];
248
249 for i in 0..n - 1 {
251 self.stack[rotation_bot_index + i] = self.stack[rotation_bot_index + i + 1];
252 }
253
254 self.stack_write(0, new_stack_top_element);
256 }
257
258 #[inline(always)]
259 fn rotate_right(&mut self, n: usize) {
260 let rotation_bot_index = self.stack_top_idx - n;
261 let new_stack_bot_element = self.stack[self.stack_top_idx - 1];
262
263 for i in 1..n {
265 self.stack[self.stack_top_idx - i] = self.stack[self.stack_top_idx - i - 1];
266 }
267
268 self.stack[rotation_bot_index] = new_stack_bot_element;
270 }
271
272 #[inline(always)]
273 fn increment_size(&mut self, tracer: &mut impl Tracer) -> Result<(), ExecutionError> {
274 if self.stack_top_idx < STACK_BUFFER_SIZE - 1 {
275 self.increment_stack_size(tracer);
276 Ok(())
277 } else {
278 Err(ExecutionError::FailedToExecuteProgram("stack overflow"))
279 }
280 }
281
282 #[inline(always)]
283 fn decrement_size(&mut self, tracer: &mut impl Tracer) {
284 self.decrement_stack_size(tracer)
285 }
286}
287
288pub struct NoopHelperRegisters;
289
290const DEFAULT_HELPERS: [Felt; NUM_USER_OP_HELPERS] = [ZERO; NUM_USER_OP_HELPERS];
293
294impl OperationHelperRegisters for NoopHelperRegisters {
295 #[inline(always)]
296 fn op_eq_registers(_a: Felt, _b: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
297 DEFAULT_HELPERS
298 }
299
300 #[inline(always)]
301 fn op_u32split_registers(_hi: Felt, _lo: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
302 DEFAULT_HELPERS
303 }
304
305 #[inline(always)]
306 fn op_eqz_registers(_top: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
307 DEFAULT_HELPERS
308 }
309
310 #[inline(always)]
311 fn op_expacc_registers(_acc_update_val: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
312 DEFAULT_HELPERS
313 }
314
315 #[inline(always)]
316 fn op_fri_ext2fold4_registers(
317 _ev: QuadFelt,
318 _es: QuadFelt,
319 _x: Felt,
320 _x_inv: Felt,
321 ) -> [Felt; NUM_USER_OP_HELPERS] {
322 DEFAULT_HELPERS
323 }
324
325 #[inline(always)]
326 fn op_u32add_registers(_hi: Felt, _lo: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
327 DEFAULT_HELPERS
328 }
329
330 #[inline(always)]
331 fn op_u32add3_registers(_hi: Felt, _lo: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
332 DEFAULT_HELPERS
333 }
334
335 #[inline(always)]
336 fn op_u32sub_registers(_second_new: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
337 DEFAULT_HELPERS
338 }
339
340 #[inline(always)]
341 fn op_u32mul_registers(_hi: Felt, _lo: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
342 DEFAULT_HELPERS
343 }
344
345 #[inline(always)]
346 fn op_u32madd_registers(_hi: Felt, _lo: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
347 DEFAULT_HELPERS
348 }
349
350 #[inline(always)]
351 fn op_u32div_registers(_hi: Felt, _lo: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
352 DEFAULT_HELPERS
353 }
354
355 #[inline(always)]
356 fn op_u32assert2_registers(_first: Felt, _second: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
357 DEFAULT_HELPERS
358 }
359
360 #[inline(always)]
361 fn op_hperm_registers(_addr: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
362 DEFAULT_HELPERS
363 }
364
365 #[inline(always)]
366 fn op_log_precompile_registers(_addr: Felt, _cap_prev: Word) -> [Felt; NUM_USER_OP_HELPERS] {
367 DEFAULT_HELPERS
368 }
369
370 #[inline(always)]
371 fn op_merkle_path_registers(_addr: Felt) -> [Felt; NUM_USER_OP_HELPERS] {
372 DEFAULT_HELPERS
373 }
374
375 #[inline(always)]
376 fn op_horner_eval_base_registers(
377 _alpha: QuadFelt,
378 _tmp0: QuadFelt,
379 _tmp1: QuadFelt,
380 ) -> [Felt; NUM_USER_OP_HELPERS] {
381 DEFAULT_HELPERS
382 }
383
384 #[inline(always)]
385 fn op_horner_eval_ext_registers(
386 _alpha: QuadFelt,
387 _k0: Felt,
388 _k1: Felt,
389 _acc_tmp: QuadFelt,
390 ) -> [Felt; NUM_USER_OP_HELPERS] {
391 DEFAULT_HELPERS
392 }
393}
394
395#[expect(clippy::too_many_arguments)]
400pub fn eval_circuit_fast_(
401 ctx: ContextId,
402 ptr: Felt,
403 clk: RowIndex,
404 num_vars: Felt,
405 num_eval: Felt,
406 mem: &mut impl MemoryInterface,
407 err_ctx: &impl ErrorContext,
408 tracer: &mut impl Tracer,
409) -> Result<CircuitEvaluation, ExecutionError> {
410 let num_vars = num_vars.as_int();
411 let num_eval = num_eval.as_int();
412
413 let num_wires = num_vars + num_eval;
414 if num_wires > MAX_NUM_ACE_WIRES as u64 {
415 return Err(ExecutionError::failed_arithmetic_evaluation(
416 err_ctx,
417 AceError::TooManyWires(num_wires),
418 ));
419 }
420
421 if !num_vars.is_multiple_of(2) || num_vars == 0 {
425 return Err(ExecutionError::failed_arithmetic_evaluation(
426 err_ctx,
427 AceError::NumVarIsNotWordAlignedOrIsEmpty(num_vars),
428 ));
429 }
430 if !num_eval.is_multiple_of(4) || num_eval == 0 {
431 return Err(ExecutionError::failed_arithmetic_evaluation(
432 err_ctx,
433 AceError::NumEvalIsNotWordAlignedOrIsEmpty(num_eval),
434 ));
435 }
436
437 let num_read_rows = num_vars as u32 / 2;
439 let num_eval_rows = num_eval as u32;
440
441 let mut evaluation_context = CircuitEvaluation::new(ctx, clk, num_read_rows, num_eval_rows);
442
443 let mut ptr = ptr;
444 for _ in 0..num_read_rows {
448 let word = mem.read_word(ctx, ptr, clk, err_ctx).map_err(ExecutionError::MemoryError)?;
449 tracer.record_memory_read_word(word, ptr, ctx, clk);
450 evaluation_context.do_read(ptr, word)?;
451 ptr += PTR_OFFSET_WORD;
452 }
453 for _ in 0..num_eval_rows {
455 let instruction =
456 mem.read_element(ctx, ptr, err_ctx).map_err(ExecutionError::MemoryError)?;
457 tracer.record_memory_read_element(instruction, ptr, ctx, clk);
458 evaluation_context.do_eval(ptr, instruction, err_ctx)?;
459 ptr += PTR_OFFSET_ELEM;
460 }
461
462 if !evaluation_context.output_value().is_some_and(|eval| eval == QuadFelt::ZERO) {
464 return Err(ExecutionError::failed_arithmetic_evaluation(
465 err_ctx,
466 AceError::CircuitNotEvaluateZero,
467 ));
468 }
469
470 Ok(evaluation_context)
471}