miden_core/operations/mod.rs
1use core::fmt;
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6mod decorators;
7pub use decorators::{
8 AssemblyOp, DebugOptions, DebugVarInfo, DebugVarLocation, Decorator, DecoratorList,
9};
10pub use opcode_constants::*;
11
12use crate::{
13 Felt,
14 serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
15};
16
17// OPERATIONS OP CODES
18// ================================================================================================
19
20/// Opcode patterns have the following meanings:
21/// - 00xxxxx operations do not shift the stack; constraint degree can be up to 2.
22/// - 010xxxx operations shift the stack the left; constraint degree can be up to 2.
23/// - 011xxxx operations shift the stack to the right; constraint degree can be up to 2.
24/// - 100xxx-: operations consume 4 range checks; constraint degree can be up to 3. These are used
25/// to encode most u32 operations.
26/// - 101xxx-: operations where constraint degree can be up to 3. These include control flow
27/// operations and some other operations requiring high degree constraints.
28/// - 11xxx--: operations where constraint degree can be up to 5. These include control flow
29/// operations and some other operations requiring very high degree constraints.
30#[rustfmt::skip]
31mod opcode_constants {
32 pub const OPCODE_NOOP: u8 = 0b0000_0000;
33 pub const OPCODE_EQZ: u8 = 0b0000_0001;
34 pub const OPCODE_NEG: u8 = 0b0000_0010;
35 pub const OPCODE_INV: u8 = 0b0000_0011;
36 pub const OPCODE_INCR: u8 = 0b0000_0100;
37 pub const OPCODE_NOT: u8 = 0b0000_0101;
38 /* unused 0b0000_0110 */
39 pub const OPCODE_MLOAD: u8 = 0b0000_0111;
40 pub const OPCODE_SWAP: u8 = 0b0000_1000;
41 pub const OPCODE_CALLER: u8 = 0b0000_1001;
42 pub const OPCODE_MOVUP2: u8 = 0b0000_1010;
43 pub const OPCODE_MOVDN2: u8 = 0b0000_1011;
44 pub const OPCODE_MOVUP3: u8 = 0b0000_1100;
45 pub const OPCODE_MOVDN3: u8 = 0b0000_1101;
46 pub const OPCODE_ADVPOPW: u8 = 0b0000_1110;
47 pub const OPCODE_EXPACC: u8 = 0b0000_1111;
48
49 pub const OPCODE_MOVUP4: u8 = 0b0001_0000;
50 pub const OPCODE_MOVDN4: u8 = 0b0001_0001;
51 pub const OPCODE_MOVUP5: u8 = 0b0001_0010;
52 pub const OPCODE_MOVDN5: u8 = 0b0001_0011;
53 pub const OPCODE_MOVUP6: u8 = 0b0001_0100;
54 pub const OPCODE_MOVDN6: u8 = 0b0001_0101;
55 pub const OPCODE_MOVUP7: u8 = 0b0001_0110;
56 pub const OPCODE_MOVDN7: u8 = 0b0001_0111;
57 pub const OPCODE_SWAPW: u8 = 0b0001_1000;
58 pub const OPCODE_EXT2MUL: u8 = 0b0001_1001;
59 pub const OPCODE_MOVUP8: u8 = 0b0001_1010;
60 pub const OPCODE_MOVDN8: u8 = 0b0001_1011;
61 pub const OPCODE_SWAPW2: u8 = 0b0001_1100;
62 pub const OPCODE_SWAPW3: u8 = 0b0001_1101;
63 pub const OPCODE_SWAPDW: u8 = 0b0001_1110;
64 pub const OPCODE_EMIT: u8 = 0b0001_1111;
65
66 pub const OPCODE_ASSERT: u8 = 0b0010_0000;
67 pub const OPCODE_EQ: u8 = 0b0010_0001;
68 pub const OPCODE_ADD: u8 = 0b0010_0010;
69 pub const OPCODE_MUL: u8 = 0b0010_0011;
70 pub const OPCODE_AND: u8 = 0b0010_0100;
71 pub const OPCODE_OR: u8 = 0b0010_0101;
72 pub const OPCODE_U32AND: u8 = 0b0010_0110;
73 pub const OPCODE_U32XOR: u8 = 0b0010_0111;
74 pub const OPCODE_FRIE2F4: u8 = 0b0010_1000;
75 pub const OPCODE_DROP: u8 = 0b0010_1001;
76 pub const OPCODE_CSWAP: u8 = 0b0010_1010;
77 pub const OPCODE_CSWAPW: u8 = 0b0010_1011;
78 pub const OPCODE_MLOADW: u8 = 0b0010_1100;
79 pub const OPCODE_MSTORE: u8 = 0b0010_1101;
80 pub const OPCODE_MSTOREW: u8 = 0b0010_1110;
81 /* unused 0b0010_1111 */
82
83 pub const OPCODE_PAD: u8 = 0b0011_0000;
84 pub const OPCODE_DUP0: u8 = 0b0011_0001;
85 pub const OPCODE_DUP1: u8 = 0b0011_0010;
86 pub const OPCODE_DUP2: u8 = 0b0011_0011;
87 pub const OPCODE_DUP3: u8 = 0b0011_0100;
88 pub const OPCODE_DUP4: u8 = 0b0011_0101;
89 pub const OPCODE_DUP5: u8 = 0b0011_0110;
90 pub const OPCODE_DUP6: u8 = 0b0011_0111;
91 pub const OPCODE_DUP7: u8 = 0b0011_1000;
92 pub const OPCODE_DUP9: u8 = 0b0011_1001;
93 pub const OPCODE_DUP11: u8 = 0b0011_1010;
94 pub const OPCODE_DUP13: u8 = 0b0011_1011;
95 pub const OPCODE_DUP15: u8 = 0b0011_1100;
96 pub const OPCODE_ADVPOP: u8 = 0b0011_1101;
97 pub const OPCODE_SDEPTH: u8 = 0b0011_1110;
98 pub const OPCODE_CLK: u8 = 0b0011_1111;
99
100 pub const OPCODE_U32ADD: u8 = 0b0100_0000;
101 pub const OPCODE_U32SUB: u8 = 0b0100_0010;
102 pub const OPCODE_U32MUL: u8 = 0b0100_0100;
103 pub const OPCODE_U32DIV: u8 = 0b0100_0110;
104 pub const OPCODE_U32SPLIT: u8 = 0b0100_1000;
105 pub const OPCODE_U32ASSERT2: u8 = 0b0100_1010;
106 pub const OPCODE_U32ADD3: u8 = 0b0100_1100;
107 pub const OPCODE_U32MADD: u8 = 0b0100_1110;
108
109 pub const OPCODE_HPERM: u8 = 0b0101_0000;
110 pub const OPCODE_MPVERIFY: u8 = 0b0101_0001;
111 pub const OPCODE_PIPE: u8 = 0b0101_0010;
112 pub const OPCODE_MSTREAM: u8 = 0b0101_0011;
113 pub const OPCODE_SPLIT: u8 = 0b0101_0100;
114 pub const OPCODE_LOOP: u8 = 0b0101_0101;
115 pub const OPCODE_SPAN: u8 = 0b0101_0110;
116 pub const OPCODE_JOIN: u8 = 0b0101_0111;
117 pub const OPCODE_DYN: u8 = 0b0101_1000;
118 pub const OPCODE_HORNERBASE: u8 = 0b0101_1001;
119 pub const OPCODE_HORNEREXT: u8 = 0b0101_1010;
120 pub const OPCODE_PUSH: u8 = 0b0101_1011;
121 pub const OPCODE_DYNCALL: u8 = 0b0101_1100;
122 pub const OPCODE_EVALCIRCUIT: u8 = 0b0101_1101;
123 pub const OPCODE_LOGPRECOMPILE: u8 = 0b0101_1110;
124
125 pub const OPCODE_MRUPDATE: u8 = 0b0110_0000;
126 pub const OPCODE_CRYPTOSTREAM: u8 = 0b0110_0100;
127 pub const OPCODE_SYSCALL: u8 = 0b0110_1000;
128 pub const OPCODE_CALL: u8 = 0b0110_1100;
129 pub const OPCODE_END: u8 = 0b0111_0000;
130 pub const OPCODE_REPEAT: u8 = 0b0111_0100;
131 pub const OPCODE_RESPAN: u8 = 0b0111_1000;
132 pub const OPCODE_HALT: u8 = 0b0111_1100;
133}
134
135// OPERATIONS
136// ================================================================================================
137
138/// A set of native VM operations which take exactly one cycle to execute.
139#[derive(Copy, Clone, Debug, Eq, PartialEq)]
140#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
141#[repr(u8)]
142pub enum Operation {
143 // ----- system operations -------------------------------------------------------------------
144 /// Advances cycle counter, but does not change the state of user stack.
145 Noop = OPCODE_NOOP,
146
147 /// Pops the stack; if the popped value is not 1, execution fails.
148 ///
149 /// The internal value specifies an error code associated with the error in case when the
150 /// execution fails.
151 Assert(Felt) = OPCODE_ASSERT,
152
153 /// Pushes the current depth of the stack onto the stack.
154 SDepth = OPCODE_SDEPTH,
155
156 /// Overwrites the top four stack items with the hash of a function which initiated the current
157 /// SYSCALL. Thus, this operation can be executed only inside a SYSCALL code block.
158 Caller = OPCODE_CALLER,
159
160 /// Pushes the current value of the clock cycle onto the stack. This operation can be used to
161 /// measure the number of cycles it has taken to execute the program up to the current
162 /// instruction.
163 Clk = OPCODE_CLK,
164
165 /// Emits an event to the host.
166 ///
167 /// Semantics:
168 /// - Reads the event id from the top of the stack (as a `Felt`) without consuming it; the
169 /// caller is responsible for pushing and later dropping the id.
170 /// - User-defined events are conventionally derived from strings via
171 /// `hash_string_to_word(name)[0]` (Blake3-based) and may be emitted via immediate forms in
172 /// assembly (`emit.event("...")` or `emit.CONST` where `CONST=event("...")`).
173 /// - System events are still identified by specific 32-bit codes; the VM attempts to interpret
174 /// the stack `Felt` as `u32` to dispatch known system events, and otherwise forwards the
175 /// event to the host.
176 ///
177 /// This operation does not change the state of the user stack aside from reading the value.
178 Emit = OPCODE_EMIT,
179
180 // ----- flow control operations -------------------------------------------------------------
181 /// Marks the beginning of a join block.
182 Join = OPCODE_JOIN,
183
184 /// Marks the beginning of a split block.
185 Split = OPCODE_SPLIT,
186
187 /// Marks the beginning of a loop block.
188 Loop = OPCODE_LOOP,
189
190 /// Marks the beginning of a function call.
191 Call = OPCODE_CALL,
192
193 /// Marks the beginning of a dynamic code block, where the target is specified by the stack.
194 Dyn = OPCODE_DYN,
195
196 /// Marks the beginning of a dynamic function call, where the target is specified by the stack.
197 Dyncall = OPCODE_DYNCALL,
198
199 /// Marks the beginning of a kernel call.
200 SysCall = OPCODE_SYSCALL,
201
202 /// Marks the beginning of a span code block.
203 Span = OPCODE_SPAN,
204
205 /// Marks the end of a program block.
206 End = OPCODE_END,
207
208 /// Indicates that body of an executing loop should be executed again.
209 Repeat = OPCODE_REPEAT,
210
211 /// Starts processing a new operation batch.
212 Respan = OPCODE_RESPAN,
213
214 /// Indicates the end of the program. This is used primarily to pad the execution trace to
215 /// the required length. Once HALT operation is executed, no other operations can be executed
216 /// by the VM (HALT operation itself excepted).
217 Halt = OPCODE_HALT,
218
219 // ----- field operations --------------------------------------------------------------------
220 /// Pops two elements off the stack, adds them, and pushes the result back onto the stack.
221 Add = OPCODE_ADD,
222
223 /// Pops an element off the stack, negates it, and pushes the result back onto the stack.
224 Neg = OPCODE_NEG,
225
226 /// Pops two elements off the stack, multiplies them, and pushes the result back onto the
227 /// stack.
228 Mul = OPCODE_MUL,
229
230 /// Pops an element off the stack, computes its multiplicative inverse, and pushes the result
231 /// back onto the stack.
232 Inv = OPCODE_INV,
233
234 /// Pops an element off the stack, adds 1 to it, and pushes the result back onto the stack.
235 Incr = OPCODE_INCR,
236
237 /// Pops two elements off the stack, multiplies them, and pushes the result back onto the
238 /// stack.
239 ///
240 /// If either of the elements is greater than 1, execution fails. This operation is equivalent
241 /// to boolean AND.
242 And = OPCODE_AND,
243
244 /// Pops two elements off the stack and subtracts their product from their sum.
245 ///
246 /// If either of the elements is greater than 1, execution fails. This operation is equivalent
247 /// to boolean OR.
248 Or = OPCODE_OR,
249
250 /// Pops an element off the stack and subtracts it from 1.
251 ///
252 /// If the element is greater than one, the execution fails. This operation is equivalent to
253 /// boolean NOT.
254 Not = OPCODE_NOT,
255
256 /// Pops two elements off the stack and compares them. If the elements are equal, pushes 1
257 /// onto the stack, otherwise pushes 0 onto the stack.
258 Eq = OPCODE_EQ,
259
260 /// Pops an element off the stack and compares it to 0. If the element is 0, pushes 1 onto
261 /// the stack, otherwise pushes 0 onto the stack.
262 Eqz = OPCODE_EQZ,
263
264 /// Computes a single turn of exponent accumulation for the given inputs. This operation can be
265 /// be used to compute a single turn of power of a field element.
266 ///
267 /// The top 4 elements of the stack are expected to be arranged as follows (form the top):
268 /// - least significant bit of the exponent in the previous trace if there's an expacc call,
269 /// otherwise ZERO
270 /// - exponent of base number `a` for this turn
271 /// - accumulated power of base number `a` so far
272 /// - number which needs to be shifted to the right
273 ///
274 /// At the end of the operation, exponent is replaced with its square, current value of power
275 /// of base number `a` on exponent is incorporated into the accumulator and the number is
276 /// shifted to the right by one bit.
277 Expacc = OPCODE_EXPACC,
278
279 // ----- ext2 operations ---------------------------------------------------------------------
280 /// Computes the product of two elements in the extension field of degree 2 and pushes the
281 /// result back onto the stack as the third and fourth elements. Pushes 0 onto the stack as
282 /// the first and second elements.
283 ///
284 /// The extension field is defined as 𝔽ₚ\[x\]/(x² - x + 2), i.e. using the
285 /// irreducible quadratic polynomial x² - x + 2 over the base field.
286 Ext2Mul = OPCODE_EXT2MUL,
287
288 // ----- u32 operations ----------------------------------------------------------------------
289 /// Pops an element off the stack, splits it into upper and lower 32-bit values, and pushes
290 /// these values back onto the stack.
291 U32split = OPCODE_U32SPLIT,
292
293 /// Pops two elements off the stack, adds them, and splits the result into upper and lower
294 /// 32-bit values. Then pushes these values back onto the stack.
295 ///
296 /// If either of these elements is greater than or equal to 2^32, the result of this
297 /// operation is undefined.
298 U32add = OPCODE_U32ADD,
299
300 /// Pops two elements off the stack and checks if each of them represents a 32-bit value.
301 /// If both of them are, they are pushed back onto the stack, otherwise an error is returned.
302 ///
303 /// The internal value specifies an error code associated with the error in case when the
304 /// assertion fails.
305 U32assert2(Felt) = OPCODE_U32ASSERT2,
306
307 /// Pops three elements off the stack, adds them together, and splits the result into upper
308 /// and lower 32-bit values. Then pushes the result back onto the stack.
309 U32add3 = OPCODE_U32ADD3,
310
311 /// Pops two elements off the stack and subtracts the first element from the second. Then,
312 /// the result, together with a flag indicating whether subtraction underflowed is pushed
313 /// onto the stack.
314 ///
315 /// If their of the values is greater than or equal to 2^32, the result of this operation is
316 /// undefined.
317 U32sub = OPCODE_U32SUB,
318
319 /// Pops two elements off the stack, multiplies them, and splits the result into upper and
320 /// lower 32-bit values. Then pushes these values back onto the stack.
321 ///
322 /// If their of the values is greater than or equal to 2^32, the result of this operation is
323 /// undefined.
324 U32mul = OPCODE_U32MUL,
325
326 /// Pops two elements off the stack and multiplies them. Then pops the third element off the
327 /// stack, and adds it to the result. Finally, splits the result into upper and lower 32-bit
328 /// values, and pushes them onto the stack.
329 ///
330 /// If any of the three values is greater than or equal to 2^32, the result of this operation
331 /// is undefined.
332 U32madd = OPCODE_U32MADD,
333
334 /// Pops two elements off the stack and divides the second element by the first. Then pushes
335 /// the integer result of the division, together with the remainder, onto the stack.
336 ///
337 /// If their of the values is greater than or equal to 2^32, the result of this operation is
338 /// undefined.
339 U32div = OPCODE_U32DIV,
340
341 /// Pops two elements off the stack, computes their binary AND, and pushes the result back
342 /// onto the stack.
343 ///
344 /// If either of the elements is greater than or equal to 2^32, execution fails.
345 U32and = OPCODE_U32AND,
346
347 /// Pops two elements off the stack, computes their binary XOR, and pushes the result back
348 /// onto the stack.
349 ///
350 /// If either of the elements is greater than or equal to 2^32, execution fails.
351 U32xor = OPCODE_U32XOR,
352
353 // ----- stack manipulation ------------------------------------------------------------------
354 /// Pushes 0 onto the stack.
355 Pad = OPCODE_PAD,
356
357 /// Removes to element from the stack.
358 Drop = OPCODE_DROP,
359
360 /// Pushes a copy of stack element 0 onto the stack.
361 Dup0 = OPCODE_DUP0,
362
363 /// Pushes a copy of stack element 1 onto the stack.
364 Dup1 = OPCODE_DUP1,
365
366 /// Pushes a copy of stack element 2 onto the stack.
367 Dup2 = OPCODE_DUP2,
368
369 /// Pushes a copy of stack element 3 onto the stack.
370 Dup3 = OPCODE_DUP3,
371
372 /// Pushes a copy of stack element 4 onto the stack.
373 Dup4 = OPCODE_DUP4,
374
375 /// Pushes a copy of stack element 5 onto the stack.
376 Dup5 = OPCODE_DUP5,
377
378 /// Pushes a copy of stack element 6 onto the stack.
379 Dup6 = OPCODE_DUP6,
380
381 /// Pushes a copy of stack element 7 onto the stack.
382 Dup7 = OPCODE_DUP7,
383
384 /// Pushes a copy of stack element 9 onto the stack.
385 Dup9 = OPCODE_DUP9,
386
387 /// Pushes a copy of stack element 11 onto the stack.
388 Dup11 = OPCODE_DUP11,
389
390 /// Pushes a copy of stack element 13 onto the stack.
391 Dup13 = OPCODE_DUP13,
392
393 /// Pushes a copy of stack element 15 onto the stack.
394 Dup15 = OPCODE_DUP15,
395
396 /// Swaps stack elements 0 and 1.
397 Swap = OPCODE_SWAP,
398
399 /// Swaps stack elements 0, 1, 2, and 3 with elements 4, 5, 6, and 7.
400 SwapW = OPCODE_SWAPW,
401
402 /// Swaps stack elements 0, 1, 2, and 3 with elements 8, 9, 10, and 11.
403 SwapW2 = OPCODE_SWAPW2,
404
405 /// Swaps stack elements 0, 1, 2, and 3, with elements 12, 13, 14, and 15.
406 SwapW3 = OPCODE_SWAPW3,
407
408 /// Swaps the top two words pair wise.
409 ///
410 /// Input: [D, C, B, A, ...]
411 /// Output: [B, A, D, C, ...]
412 SwapDW = OPCODE_SWAPDW,
413
414 /// Moves stack element 2 to the top of the stack.
415 MovUp2 = OPCODE_MOVUP2,
416
417 /// Moves stack element 3 to the top of the stack.
418 MovUp3 = OPCODE_MOVUP3,
419
420 /// Moves stack element 4 to the top of the stack.
421 MovUp4 = OPCODE_MOVUP4,
422
423 /// Moves stack element 5 to the top of the stack.
424 MovUp5 = OPCODE_MOVUP5,
425
426 /// Moves stack element 6 to the top of the stack.
427 MovUp6 = OPCODE_MOVUP6,
428
429 /// Moves stack element 7 to the top of the stack.
430 MovUp7 = OPCODE_MOVUP7,
431
432 /// Moves stack element 8 to the top of the stack.
433 MovUp8 = OPCODE_MOVUP8,
434
435 /// Moves the top stack element to position 2 on the stack.
436 MovDn2 = OPCODE_MOVDN2,
437
438 /// Moves the top stack element to position 3 on the stack.
439 MovDn3 = OPCODE_MOVDN3,
440
441 /// Moves the top stack element to position 4 on the stack.
442 MovDn4 = OPCODE_MOVDN4,
443
444 /// Moves the top stack element to position 5 on the stack.
445 MovDn5 = OPCODE_MOVDN5,
446
447 /// Moves the top stack element to position 6 on the stack.
448 MovDn6 = OPCODE_MOVDN6,
449
450 /// Moves the top stack element to position 7 on the stack.
451 MovDn7 = OPCODE_MOVDN7,
452
453 /// Moves the top stack element to position 8 on the stack.
454 MovDn8 = OPCODE_MOVDN8,
455
456 /// Pops an element off the stack, and if the element is 1, swaps the top two remaining
457 /// elements on the stack. If the popped element is 0, the stack remains unchanged.
458 ///
459 /// If the popped element is neither 0 nor 1, execution fails.
460 CSwap = OPCODE_CSWAP,
461
462 /// Pops an element off the stack, and if the element is 1, swaps the remaining elements
463 /// 0, 1, 2, and 3 with elements 4, 5, 6, and 7. If the popped element is 0, the stack
464 /// remains unchanged.
465 ///
466 /// If the popped element is neither 0 nor 1, execution fails.
467 CSwapW = OPCODE_CSWAPW,
468
469 // ----- input / output ----------------------------------------------------------------------
470 /// Pushes the immediate value onto the stack.
471 Push(Felt) = OPCODE_PUSH,
472
473 /// Removes the next element from the advice stack and pushes it onto the operand stack.
474 AdvPop = OPCODE_ADVPOP,
475
476 /// Removes a word (4 elements) from the advice stack and overwrites the top four operand
477 /// stack elements with it.
478 AdvPopW = OPCODE_ADVPOPW,
479
480 /// Pops an element off the stack, interprets it as a memory address, and replaces the
481 /// remaining 4 elements at the top of the stack with values located at the specified address.
482 MLoadW = OPCODE_MLOADW,
483
484 /// Pops an element off the stack, interprets it as a memory address, and writes the remaining
485 /// 4 elements at the top of the stack into memory at the specified address.
486 MStoreW = OPCODE_MSTOREW,
487
488 /// Pops an element off the stack, interprets it as a memory address, and pushes the first
489 /// element of the word located at the specified address to the stack.
490 MLoad = OPCODE_MLOAD,
491
492 /// Pops an element off the stack, interprets it as a memory address, and writes the remaining
493 /// element at the top of the stack into the first element of the word located at the specified
494 /// memory address. The remaining 3 elements of the word are not affected.
495 MStore = OPCODE_MSTORE,
496
497 /// Loads two words from memory, and replaces the top 8 elements of the stack with them,
498 /// element-wise, in stack order.
499 ///
500 /// The operation works as follows:
501 /// - The memory address of the first word is retrieved from 13th stack element (position 12).
502 /// - Two consecutive words, starting at this address, are loaded from memory.
503 /// - The top 8 elements of the stack are overwritten with these words (element-wise, in stack
504 /// order).
505 /// - Memory address (in position 12) is incremented by 2.
506 /// - All other stack elements remain the same.
507 MStream = OPCODE_MSTREAM,
508
509 /// Pops two words from the advice stack, writes them to memory, and replaces the top 8
510 /// elements of the stack with them, element-wise, in stack order.
511 ///
512 /// The operation works as follows:
513 /// - Two words are popped from the advice stack.
514 /// - The destination memory address for the first word is retrieved from the 13th stack element
515 /// (position 12).
516 /// - The two words are written to memory consecutively, starting at this address.
517 /// - The top 8 elements of the stack are overwritten with these words (element-wise, in stack
518 /// order).
519 /// - Memory address (in position 12) is incremented by 2.
520 /// - All other stack elements remain the same.
521 Pipe = OPCODE_PIPE,
522
523 /// Encrypts data from source memory to destination memory using the Poseidon2 sponge keystream.
524 ///
525 /// Two consecutive words (8 elements) are loaded from source memory, each element is added
526 /// to the corresponding element in the rate (top 8 stack elements), and the resulting
527 /// ciphertext is written to destination memory and replaces the rate. Source and destination
528 /// addresses are incremented by 8.
529 ///
530 /// Stack transition:
531 /// ```text
532 /// [rate(8), cap(4), src, dst, ...]
533 /// ↓
534 /// [ct(8), cap(4), src+8, dst+8, ...]
535 /// ```
536 /// where `ct = mem[src..src+8] + rate`, where addition is element-wise.
537 ///
538 /// After this operation, `hperm` should be applied to refresh the keystream for the next block.
539 CryptoStream = OPCODE_CRYPTOSTREAM,
540
541 // ----- cryptographic operations ------------------------------------------------------------
542 /// Performs a Poseidon2 permutation on the top 3 words of the operand stack,
543 /// where the top 2 words are the rate (words C and B), the deepest word is the capacity (word
544 /// A), and the digest output is the middle word E.
545 ///
546 /// Stack transition:
547 /// [C, B, A, ...] -> [F, E, D, ...]
548 HPerm = OPCODE_HPERM,
549
550 /// Verifies that a Merkle path from the specified node resolves to the specified root. This
551 /// operation can be used to prove that the prover knows a path in the specified Merkle tree
552 /// which starts with the specified node.
553 ///
554 /// The stack is expected to be arranged as follows (from the top):
555 /// - value of the node, 4 elements.
556 /// - depth of the path, 1 element.
557 /// - index of the node, 1 element.
558 /// - root of the tree, 4 elements.
559 ///
560 /// The Merkle path itself is expected to be provided by the prover non-deterministically (via
561 /// merkle sets). If the prover is not able to provide the required path, the operation fails.
562 /// The state of the stack does not change.
563 ///
564 /// The internal value specifies an error code associated with the error in case when the
565 /// assertion fails.
566 MpVerify(Felt) = OPCODE_MPVERIFY,
567
568 /// Computes a new root of a Merkle tree where a node at the specified position is updated to
569 /// the specified value.
570 ///
571 /// The stack is expected to be arranged as follows (from the top):
572 /// - old value of the node, 4 element
573 /// - depth of the node, 1 element
574 /// - index of the node, 1 element
575 /// - current root of the tree, 4 elements
576 /// - new value of the node, 4 element
577 ///
578 /// The Merkle path for the node is expected to be provided by the prover non-deterministically
579 /// via the advice provider. At the end of the operation, the old node value is replaced with
580 /// the new root value, that is computed based on the provided path. Everything else on the
581 /// stack remains the same.
582 ///
583 /// The tree will always be copied into a new instance, meaning the advice provider will keep
584 /// track of both the old and new Merkle trees.
585 MrUpdate = OPCODE_MRUPDATE,
586
587 /// Performs FRI (Fast Reed-Solomon Interactive Oracle Proofs) layer folding by a factor of 4
588 /// for FRI protocol executed in a degree 2 extension of the base field.
589 ///
590 /// This operation:
591 /// - Folds 4 query values (v0, v1), (v2, v3), (v4, v5), (v6, v7) into a single value (ne0, ne1)
592 /// - Computes new value of the domain generator power: poe' = poe^4
593 /// - Increments layer pointer (cptr) by 2
594 /// - Checks that the previous folding was done correctly
595 /// - Shifts the stack to move an item from the overflow table to stack position 15
596 ///
597 /// Stack transition:
598 /// Input: [v7, v6, v5, v4, v3, v2, v1, v0, f_pos, d_seg, poe, pe1, pe0, a1, a0, cptr, ...]
599 /// Output: [t1, t0, s1, s0, df3, df2, df1, df0, poe^2, f_tau, cptr+2, poe^4, f_pos, ne1, ne0,
600 /// eptr, ...] where eptr is moved from the stack overflow table and is the address of the
601 /// final FRI layer.
602 FriE2F4 = OPCODE_FRIE2F4,
603
604 /// Performs 8 steps of the Horner evaluation method on a polynomial with coefficients over
605 /// the base field, i.e., it computes
606 ///
607 /// acc' = (((acc_tmp * alpha + c3) * alpha + c2) * alpha + c1) * alpha + c0
608 ///
609 /// where
610 ///
611 /// acc_tmp := (((acc * alpha + c7) * alpha + c6) * alpha + c5) * alpha + c4
612 ///
613 ///
614 /// In other words, the intsruction computes the evaluation at alpha of the polynomial
615 ///
616 /// P(X) := c7 * X^7 + c6 * X^6 + ... + c1 * X + c0
617 HornerBase = OPCODE_HORNERBASE,
618
619 /// Performs 4 steps of the Horner evaluation method on a polynomial with coefficients over
620 /// the extension field, i.e., it computes
621 ///
622 /// acc' = (((acc * alpha + c3) * alpha + c2) * alpha + c1) * alpha + c0
623 ///
624 /// In other words, the intsruction computes the evaluation at alpha of the polynomial
625 ///
626 /// P(X) := c3 * X^3 + c2 * X^2 + c1 * X + c0
627 HornerExt = OPCODE_HORNEREXT,
628
629 /// Evaluates an arithmetic circuit given a pointer to its description in memory, the number
630 /// of arithmetic gates, and the sum of the input and constant gates.
631 EvalCircuit = OPCODE_EVALCIRCUIT,
632
633 /// Logs a precompile event. This instruction is used to signal that a precompile computation
634 /// was requested.
635 LogPrecompile = OPCODE_LOGPRECOMPILE,
636}
637
638impl Operation {
639 pub const OP_BITS: usize = 7;
640
641 /// Returns the opcode of this operation.
642 #[rustfmt::skip]
643 pub fn op_code(&self) -> u8 {
644 // SAFETY: This is safe because we have given this enum a primitive representation with
645 // #[repr(u8)], with the first field of the underlying union-of-structs the discriminant.
646 //
647 // See the section on "accessing the numeric value of the discriminant"
648 // here: https://doc.rust-lang.org/std/mem/fn.discriminant.html
649 unsafe { *<*const _>::from(self).cast::<u8>() }
650 }
651
652 /// Returns an immediate value carried by this operation.
653 // Proptest generators for operations in crate::mast::node::basic_block_node::tests discriminate
654 // on this flag, please update them when you modify the semantics of this method.
655 pub fn imm_value(&self) -> Option<Felt> {
656 match *self {
657 Self::Push(imm) => Some(imm),
658 _ => None,
659 }
660 }
661
662 /// Returns true if this operation writes any data to the decoder hasher registers.
663 ///
664 /// In other words, if so, then the user op helper registers are not available.
665 pub fn populates_decoder_hasher_registers(&self) -> bool {
666 matches!(
667 self,
668 Self::End
669 | Self::Join
670 | Self::Split
671 | Self::Loop
672 | Self::Repeat
673 | Self::Respan
674 | Self::Span
675 | Self::Halt
676 | Self::Call
677 | Self::SysCall
678 )
679 }
680}
681
682impl crate::prettier::PrettyPrint for Operation {
683 fn render(&self) -> crate::prettier::Document {
684 crate::prettier::display(self)
685 }
686}
687
688impl fmt::Display for Operation {
689 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
690 match self {
691 // ----- system operations ------------------------------------------------------------
692 Self::Noop => write!(f, "noop"),
693 Self::Assert(err_code) => write!(f, "assert({err_code})"),
694
695 Self::SDepth => write!(f, "sdepth"),
696 Self::Caller => write!(f, "caller"),
697
698 Self::Clk => write!(f, "clk"),
699
700 // ----- flow control operations ------------------------------------------------------
701 Self::Join => write!(f, "join"),
702 Self::Split => write!(f, "split"),
703 Self::Loop => write!(f, "loop"),
704 Self::Call => writeln!(f, "call"),
705 Self::Dyncall => writeln!(f, "dyncall"),
706 Self::SysCall => writeln!(f, "syscall"),
707 Self::Dyn => writeln!(f, "dyn"),
708 Self::Span => write!(f, "span"),
709 Self::End => write!(f, "end"),
710 Self::Repeat => write!(f, "repeat"),
711 Self::Respan => write!(f, "respan"),
712 Self::Halt => write!(f, "halt"),
713
714 // ----- field operations -------------------------------------------------------------
715 Self::Add => write!(f, "add"),
716 Self::Neg => write!(f, "neg"),
717 Self::Mul => write!(f, "mul"),
718 Self::Inv => write!(f, "inv"),
719 Self::Incr => write!(f, "incr"),
720
721 Self::And => write!(f, "and"),
722 Self::Or => write!(f, "or"),
723 Self::Not => write!(f, "not"),
724
725 Self::Eq => write!(f, "eq"),
726 Self::Eqz => write!(f, "eqz"),
727
728 Self::Expacc => write!(f, "expacc"),
729
730 // ----- ext2 operations --------------------------------------------------------------
731 Self::Ext2Mul => write!(f, "ext2mul"),
732
733 // ----- u32 operations ---------------------------------------------------------------
734 Self::U32assert2(err_code) => write!(f, "u32assert2({err_code})"),
735 Self::U32split => write!(f, "u32split"),
736 Self::U32add => write!(f, "u32add"),
737 Self::U32add3 => write!(f, "u32add3"),
738 Self::U32sub => write!(f, "u32sub"),
739 Self::U32mul => write!(f, "u32mul"),
740 Self::U32madd => write!(f, "u32madd"),
741 Self::U32div => write!(f, "u32div"),
742
743 Self::U32and => write!(f, "u32and"),
744 Self::U32xor => write!(f, "u32xor"),
745
746 // ----- stack manipulation -----------------------------------------------------------
747 Self::Drop => write!(f, "drop"),
748 Self::Pad => write!(f, "pad"),
749
750 Self::Dup0 => write!(f, "dup0"),
751 Self::Dup1 => write!(f, "dup1"),
752 Self::Dup2 => write!(f, "dup2"),
753 Self::Dup3 => write!(f, "dup3"),
754 Self::Dup4 => write!(f, "dup4"),
755 Self::Dup5 => write!(f, "dup5"),
756 Self::Dup6 => write!(f, "dup6"),
757 Self::Dup7 => write!(f, "dup7"),
758 Self::Dup9 => write!(f, "dup9"),
759 Self::Dup11 => write!(f, "dup11"),
760 Self::Dup13 => write!(f, "dup13"),
761 Self::Dup15 => write!(f, "dup15"),
762
763 Self::Swap => write!(f, "swap"),
764 Self::SwapW => write!(f, "swapw"),
765 Self::SwapW2 => write!(f, "swapw2"),
766 Self::SwapW3 => write!(f, "swapw3"),
767 Self::SwapDW => write!(f, "swapdw"),
768
769 Self::MovUp2 => write!(f, "movup2"),
770 Self::MovUp3 => write!(f, "movup3"),
771 Self::MovUp4 => write!(f, "movup4"),
772 Self::MovUp5 => write!(f, "movup5"),
773 Self::MovUp6 => write!(f, "movup6"),
774 Self::MovUp7 => write!(f, "movup7"),
775 Self::MovUp8 => write!(f, "movup8"),
776
777 Self::MovDn2 => write!(f, "movdn2"),
778 Self::MovDn3 => write!(f, "movdn3"),
779 Self::MovDn4 => write!(f, "movdn4"),
780 Self::MovDn5 => write!(f, "movdn5"),
781 Self::MovDn6 => write!(f, "movdn6"),
782 Self::MovDn7 => write!(f, "movdn7"),
783 Self::MovDn8 => write!(f, "movdn8"),
784
785 Self::CSwap => write!(f, "cswap"),
786 Self::CSwapW => write!(f, "cswapw"),
787
788 // ----- input / output ---------------------------------------------------------------
789 Self::Push(value) => write!(f, "push({value})"),
790
791 Self::AdvPop => write!(f, "advpop"),
792 Self::AdvPopW => write!(f, "advpopw"),
793
794 Self::MLoadW => write!(f, "mloadw"),
795 Self::MStoreW => write!(f, "mstorew"),
796
797 Self::MLoad => write!(f, "mload"),
798 Self::MStore => write!(f, "mstore"),
799
800 Self::MStream => write!(f, "mstream"),
801 Self::Pipe => write!(f, "pipe"),
802 Self::CryptoStream => write!(f, "crypto_stream"),
803
804 Self::Emit => write!(f, "emit"),
805
806 // ----- cryptographic operations -----------------------------------------------------
807 Self::HPerm => write!(f, "hperm"),
808 Self::MpVerify(err_code) => write!(f, "mpverify({err_code})"),
809 Self::MrUpdate => write!(f, "mrupdate"),
810
811 // ----- STARK proof verification -----------------------------------------------------
812 Self::FriE2F4 => write!(f, "frie2f4"),
813 Self::HornerBase => write!(f, "horner_eval_base"),
814 Self::HornerExt => write!(f, "horner_eval_ext"),
815 Self::EvalCircuit => write!(f, "eval_circuit"),
816 Self::LogPrecompile => write!(f, "log_precompile"),
817 }
818 }
819}
820
821impl Serializable for Operation {
822 fn write_into<W: ByteWriter>(&self, target: &mut W) {
823 target.write_u8(self.op_code());
824
825 // For operations that have extra data, encode it in `data`.
826 match self {
827 Operation::Assert(err_code)
828 | Operation::MpVerify(err_code)
829 | Operation::U32assert2(err_code) => {
830 err_code.write_into(target);
831 },
832 Operation::Push(value) => value.as_canonical_u64().write_into(target),
833
834 // Note: we explicitly write out all the operations so that whenever we make a
835 // modification to the `Operation` enum, we get a compile error here. This
836 // should help us remember to properly encode/decode each operation variant.
837 Operation::Noop
838 | Operation::SDepth
839 | Operation::Caller
840 | Operation::Clk
841 | Operation::Join
842 | Operation::Split
843 | Operation::Loop
844 | Operation::Call
845 | Operation::Dyn
846 | Operation::Dyncall
847 | Operation::SysCall
848 | Operation::Span
849 | Operation::End
850 | Operation::Repeat
851 | Operation::Respan
852 | Operation::Halt
853 | Operation::Add
854 | Operation::Neg
855 | Operation::Mul
856 | Operation::Inv
857 | Operation::Incr
858 | Operation::And
859 | Operation::Or
860 | Operation::Not
861 | Operation::Eq
862 | Operation::Eqz
863 | Operation::Expacc
864 | Operation::Ext2Mul
865 | Operation::U32split
866 | Operation::U32add
867 | Operation::U32add3
868 | Operation::U32sub
869 | Operation::U32mul
870 | Operation::U32madd
871 | Operation::U32div
872 | Operation::U32and
873 | Operation::U32xor
874 | Operation::Pad
875 | Operation::Drop
876 | Operation::Dup0
877 | Operation::Dup1
878 | Operation::Dup2
879 | Operation::Dup3
880 | Operation::Dup4
881 | Operation::Dup5
882 | Operation::Dup6
883 | Operation::Dup7
884 | Operation::Dup9
885 | Operation::Dup11
886 | Operation::Dup13
887 | Operation::Dup15
888 | Operation::Swap
889 | Operation::SwapW
890 | Operation::SwapW2
891 | Operation::SwapW3
892 | Operation::SwapDW
893 | Operation::Emit
894 | Operation::MovUp2
895 | Operation::MovUp3
896 | Operation::MovUp4
897 | Operation::MovUp5
898 | Operation::MovUp6
899 | Operation::MovUp7
900 | Operation::MovUp8
901 | Operation::MovDn2
902 | Operation::MovDn3
903 | Operation::MovDn4
904 | Operation::MovDn5
905 | Operation::MovDn6
906 | Operation::MovDn7
907 | Operation::MovDn8
908 | Operation::CSwap
909 | Operation::CSwapW
910 | Operation::AdvPop
911 | Operation::AdvPopW
912 | Operation::MLoadW
913 | Operation::MStoreW
914 | Operation::MLoad
915 | Operation::MStore
916 | Operation::MStream
917 | Operation::Pipe
918 | Operation::CryptoStream
919 | Operation::HPerm
920 | Operation::MrUpdate
921 | Operation::FriE2F4
922 | Operation::HornerBase
923 | Operation::HornerExt
924 | Operation::EvalCircuit
925 | Operation::LogPrecompile => (),
926 }
927 }
928}
929
930impl Deserializable for Operation {
931 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
932 let op_code = source.read_u8()?;
933
934 let operation = match op_code {
935 OPCODE_NOOP => Self::Noop,
936 OPCODE_EQZ => Self::Eqz,
937 OPCODE_NEG => Self::Neg,
938 OPCODE_INV => Self::Inv,
939 OPCODE_INCR => Self::Incr,
940 OPCODE_NOT => Self::Not,
941 OPCODE_MLOAD => Self::MLoad,
942 OPCODE_SWAP => Self::Swap,
943 OPCODE_CALLER => Self::Caller,
944 OPCODE_MOVUP2 => Self::MovUp2,
945 OPCODE_MOVDN2 => Self::MovDn2,
946 OPCODE_MOVUP3 => Self::MovUp3,
947 OPCODE_MOVDN3 => Self::MovDn3,
948 OPCODE_ADVPOPW => Self::AdvPopW,
949 OPCODE_EXPACC => Self::Expacc,
950
951 OPCODE_MOVUP4 => Self::MovUp4,
952 OPCODE_MOVDN4 => Self::MovDn4,
953 OPCODE_MOVUP5 => Self::MovUp5,
954 OPCODE_MOVDN5 => Self::MovDn5,
955 OPCODE_MOVUP6 => Self::MovUp6,
956 OPCODE_MOVDN6 => Self::MovDn6,
957 OPCODE_MOVUP7 => Self::MovUp7,
958 OPCODE_MOVDN7 => Self::MovDn7,
959 OPCODE_SWAPW => Self::SwapW,
960 OPCODE_EXT2MUL => Self::Ext2Mul,
961 OPCODE_MOVUP8 => Self::MovUp8,
962 OPCODE_MOVDN8 => Self::MovDn8,
963 OPCODE_SWAPW2 => Self::SwapW2,
964 OPCODE_SWAPW3 => Self::SwapW3,
965 OPCODE_SWAPDW => Self::SwapDW,
966 OPCODE_EMIT => Self::Emit,
967
968 OPCODE_ASSERT => Self::Assert(Felt::read_from(source)?),
969 OPCODE_EQ => Self::Eq,
970 OPCODE_ADD => Self::Add,
971 OPCODE_MUL => Self::Mul,
972 OPCODE_AND => Self::And,
973 OPCODE_OR => Self::Or,
974 OPCODE_U32AND => Self::U32and,
975 OPCODE_U32XOR => Self::U32xor,
976 OPCODE_FRIE2F4 => Self::FriE2F4,
977 OPCODE_DROP => Self::Drop,
978 OPCODE_CSWAP => Self::CSwap,
979 OPCODE_CSWAPW => Self::CSwapW,
980 OPCODE_MLOADW => Self::MLoadW,
981 OPCODE_MSTORE => Self::MStore,
982 OPCODE_MSTOREW => Self::MStoreW,
983
984 OPCODE_PAD => Self::Pad,
985 OPCODE_DUP0 => Self::Dup0,
986 OPCODE_DUP1 => Self::Dup1,
987 OPCODE_DUP2 => Self::Dup2,
988 OPCODE_DUP3 => Self::Dup3,
989 OPCODE_DUP4 => Self::Dup4,
990 OPCODE_DUP5 => Self::Dup5,
991 OPCODE_DUP6 => Self::Dup6,
992 OPCODE_DUP7 => Self::Dup7,
993 OPCODE_DUP9 => Self::Dup9,
994 OPCODE_DUP11 => Self::Dup11,
995 OPCODE_DUP13 => Self::Dup13,
996 OPCODE_DUP15 => Self::Dup15,
997 OPCODE_ADVPOP => Self::AdvPop,
998 OPCODE_SDEPTH => Self::SDepth,
999 OPCODE_CLK => Self::Clk,
1000
1001 OPCODE_U32ADD => Self::U32add,
1002 OPCODE_U32SUB => Self::U32sub,
1003 OPCODE_U32MUL => Self::U32mul,
1004 OPCODE_U32DIV => Self::U32div,
1005 OPCODE_U32SPLIT => Self::U32split,
1006 OPCODE_U32ASSERT2 => Self::U32assert2(Felt::read_from(source)?),
1007 OPCODE_U32ADD3 => Self::U32add3,
1008 OPCODE_U32MADD => Self::U32madd,
1009
1010 OPCODE_HPERM => Self::HPerm,
1011 OPCODE_MPVERIFY => Self::MpVerify(Felt::read_from(source)?),
1012 OPCODE_PIPE => Self::Pipe,
1013 OPCODE_MSTREAM => Self::MStream,
1014 OPCODE_CRYPTOSTREAM => Self::CryptoStream,
1015 OPCODE_SPLIT => Self::Split,
1016 OPCODE_LOOP => Self::Loop,
1017 OPCODE_SPAN => Self::Span,
1018 OPCODE_JOIN => Self::Join,
1019 OPCODE_DYN => Self::Dyn,
1020 OPCODE_DYNCALL => Self::Dyncall,
1021 OPCODE_HORNERBASE => Self::HornerBase,
1022 OPCODE_HORNEREXT => Self::HornerExt,
1023 OPCODE_LOGPRECOMPILE => Self::LogPrecompile,
1024 OPCODE_EVALCIRCUIT => Self::EvalCircuit,
1025
1026 OPCODE_MRUPDATE => Self::MrUpdate,
1027 OPCODE_PUSH => Self::Push(Felt::read_from(source)?),
1028 OPCODE_SYSCALL => Self::SysCall,
1029 OPCODE_CALL => Self::Call,
1030 OPCODE_END => Self::End,
1031 OPCODE_REPEAT => Self::Repeat,
1032 OPCODE_RESPAN => Self::Respan,
1033 OPCODE_HALT => Self::Halt,
1034 _ => {
1035 return Err(DeserializationError::InvalidValue(format!(
1036 "Invalid opcode '{op_code}'"
1037 )));
1038 },
1039 };
1040
1041 Ok(operation)
1042 }
1043
1044 /// Returns the minimum serialized size: 1 byte opcode.
1045 ///
1046 /// Some operations have additional payload (e.g., Push has 8 bytes for Felt),
1047 /// but the minimum is just the opcode byte.
1048 fn min_serialized_size() -> usize {
1049 1
1050 }
1051}