miden_core/operations/
mod.rs

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