Skip to main content

miden_core/operations/
mod.rs

1use core::fmt;
2
3mod debug_metadata;
4pub use debug_metadata::AssemblyOp;
5
6use crate::{
7    Felt,
8    serde::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable},
9};
10
11// OPERATIONS AND CONTROL FLOW OPCODES
12// ================================================================================================
13
14/// Opcode patterns have the following meanings:
15/// - 00xxxxx operations do not shift the stack; constraint degree can be up to 2.
16/// - 010xxxx operations shift the stack the left; constraint degree can be up to 2.
17/// - 011xxxx operations shift the stack to the right; constraint degree can be up to 2.
18/// - 100xxx-: operations consume 4 range checks; constraint degree can be up to 3. These are used
19///   to encode most u32 operations.
20/// - 101xxx-: operations where constraint degree can be up to 3. These include control flow
21///   operations and some other operations requiring high degree constraints.
22/// - 11xxx--: operations where constraint degree can be up to 5. These include control flow
23///   operations and some other operations requiring very high degree constraints.
24#[rustfmt::skip]
25pub mod opcodes {
26    pub const NOOP: u8           = 0b0000_0000;
27    pub const EQZ: u8            = 0b0000_0001;
28    pub const NEG: u8            = 0b0000_0010;
29    pub const INV: u8            = 0b0000_0011;
30    pub const INCR: u8           = 0b0000_0100;
31    pub const NOT: u8            = 0b0000_0101;
32    /* unused                      0b0000_0110 */
33    pub const MLOAD: u8          = 0b0000_0111;
34    pub const SWAP: u8           = 0b0000_1000;
35    pub const CALLER: u8         = 0b0000_1001;
36    pub const MOVUP2: u8         = 0b0000_1010;
37    pub const MOVDN2: u8         = 0b0000_1011;
38    pub const MOVUP3: u8         = 0b0000_1100;
39    pub const MOVDN3: u8         = 0b0000_1101;
40    pub const ADVPOPW: u8        = 0b0000_1110;
41    pub const EXPACC: u8         = 0b0000_1111;
42
43    pub const MOVUP4: u8         = 0b0001_0000;
44    pub const MOVDN4: u8         = 0b0001_0001;
45    pub const MOVUP5: u8         = 0b0001_0010;
46    pub const MOVDN5: u8         = 0b0001_0011;
47    pub const MOVUP6: u8         = 0b0001_0100;
48    pub const MOVDN6: u8         = 0b0001_0101;
49    pub const MOVUP7: u8         = 0b0001_0110;
50    pub const MOVDN7: u8         = 0b0001_0111;
51    pub const SWAPW: u8          = 0b0001_1000;
52    pub const EXT2MUL: u8        = 0b0001_1001;
53    pub const MOVUP8: u8         = 0b0001_1010;
54    pub const MOVDN8: u8         = 0b0001_1011;
55    pub const SWAPW2: u8         = 0b0001_1100;
56    pub const SWAPW3: u8         = 0b0001_1101;
57    pub const SWAPDW: u8         = 0b0001_1110;
58    pub const EMIT: u8           = 0b0001_1111;
59
60    pub const ASSERT: u8         = 0b0010_0000;
61    pub const EQ: u8             = 0b0010_0001;
62    pub const ADD: u8            = 0b0010_0010;
63    pub const MUL: u8            = 0b0010_0011;
64    pub const AND: u8            = 0b0010_0100;
65    pub const OR: u8             = 0b0010_0101;
66    pub const U32AND: u8         = 0b0010_0110;
67    pub const U32XOR: u8         = 0b0010_0111;
68    pub const FRIE2F4: u8        = 0b0010_1000;
69    pub const DROP: u8           = 0b0010_1001;
70    pub const CSWAP: u8          = 0b0010_1010;
71    pub const CSWAPW: u8         = 0b0010_1011;
72    pub const MLOADW: u8         = 0b0010_1100;
73    pub const MSTORE: u8         = 0b0010_1101;
74    pub const MSTOREW: u8        = 0b0010_1110;
75    /* unused                      0b0010_1111 */
76
77    pub const PAD: u8            = 0b0011_0000;
78    pub const DUP0: u8           = 0b0011_0001;
79    pub const DUP1: u8           = 0b0011_0010;
80    pub const DUP2: u8           = 0b0011_0011;
81    pub const DUP3: u8           = 0b0011_0100;
82    pub const DUP4: u8           = 0b0011_0101;
83    pub const DUP5: u8           = 0b0011_0110;
84    pub const DUP6: u8           = 0b0011_0111;
85    pub const DUP7: u8           = 0b0011_1000;
86    pub const DUP9: u8           = 0b0011_1001;
87    pub const DUP11: u8          = 0b0011_1010;
88    pub const DUP13: u8          = 0b0011_1011;
89    pub const DUP15: u8          = 0b0011_1100;
90    pub const ADVPOP: u8         = 0b0011_1101;
91    pub const SDEPTH: u8         = 0b0011_1110;
92    pub const CLK: u8            = 0b0011_1111;
93
94    pub const U32ADD: u8         = 0b0100_0000;
95    pub const U32SUB: u8         = 0b0100_0010;
96    pub const U32MUL: u8         = 0b0100_0100;
97    pub const U32DIV: u8         = 0b0100_0110;
98    pub const U32SPLIT: u8       = 0b0100_1000;
99    pub const U32ASSERT2: u8     = 0b0100_1010;
100    pub const U32ADD3: u8        = 0b0100_1100;
101    pub const U32MADD: u8        = 0b0100_1110;
102
103    pub const COMPRESS: u8      = 0b0101_0000;
104    pub const MPVERIFY: u8       = 0b0101_0001;
105    pub const PIPE: u8           = 0b0101_0010;
106    pub const MSTREAM: u8        = 0b0101_0011;
107    pub const SPLIT: u8          = 0b0101_0100;
108    pub const LOOP: u8           = 0b0101_0101;
109    pub const SPAN: u8           = 0b0101_0110;
110    pub const JOIN: u8           = 0b0101_0111;
111    pub const DYN: u8            = 0b0101_1000;
112    pub const HORNERBASE: u8     = 0b0101_1001;
113    pub const HORNEREXT: u8      = 0b0101_1010;
114    pub const PUSH: u8           = 0b0101_1011;
115    pub const DYNCALL: u8        = 0b0101_1100;
116    pub const EVALCIRCUIT: u8    = 0b0101_1101;
117    pub const LOGDEFERRED: u8  = 0b0101_1110;
118
119    pub const MRUPDATE: u8       = 0b0110_0000;
120    pub const CRYPTOSTREAM: u8   = 0b0110_0100;
121    pub const SYSCALL: u8        = 0b0110_1000;
122    pub const CALL: u8           = 0b0110_1100;
123    pub const END: u8            = 0b0111_0000;
124    pub const REPEAT: u8         = 0b0111_0100;
125    pub const RESPAN: u8         = 0b0111_1000;
126    pub const HALT: u8           = 0b0111_1100;
127}
128
129// OPERATIONS
130// ================================================================================================
131
132/// The set of native VM basic block operations executable which take exactly one cycle to execute.
133///
134/// Specifically, the operations encoded here are only those which can be executed within basic
135/// blocks, i.e., they exclude all control flow operations (e.g., `Loop`, `Span`, `Join`, etc.).
136/// Note though that those operations have their own unique opcode which lives in the same 7-bit
137/// opcode space as the basic block operations.
138#[derive(Copy, Clone, Debug, Eq, PartialEq)]
139#[repr(u8)]
140#[cfg_attr(
141    all(feature = "arbitrary", test),
142    miden_test_serialization_macros::serialization_test
143)]
144pub enum Operation {
145    // ----- system operations -------------------------------------------------------------------
146    /// Advances cycle counter, but does not change the state of user stack.
147    Noop = opcodes::NOOP,
148
149    /// Pops the stack; if the popped value is not 1, execution fails.
150    ///
151    /// The internal value specifies an error code associated with the error in case when the
152    /// execution fails.
153    Assert(Felt) = opcodes::ASSERT,
154
155    /// Pushes the current depth of the stack onto the stack.
156    SDepth = opcodes::SDEPTH,
157
158    /// Overwrites the top four stack items with the hash of a function which initiated the current
159    /// SYSCALL. Thus, this operation can be executed only inside a SYSCALL code block.
160    Caller = opcodes::CALLER,
161
162    /// Pushes the current value of the clock cycle onto the stack. This operation can be used to
163    /// measure the number of cycles it has taken to execute the program up to the current
164    /// instruction.
165    Clk = opcodes::CLK,
166
167    /// Emits an event to the host.
168    ///
169    /// Semantics:
170    /// - Reads the event id from the top of the stack (as a `Felt`) without consuming it; the
171    ///   caller is responsible for pushing and later dropping the id.
172    /// - User-defined events are conventionally derived from strings via
173    ///   `hash_string_to_word(name)[0]` (Blake3-based) and may be emitted via immediate forms in
174    ///   assembly (`emit.event("...")` or `emit.CONST` where `CONST=event("...")`).
175    /// - System events are identified by reserved [`SystemEvent`](crate::events::SystemEvent) IDs.
176    ///   Most are handled by the VM; `SystemEvent::TraceEvent` triggers the host's optional
177    ///   read-only trace handler for the trace event id at stack position 1. Assembly exposes this
178    ///   through `trace`, `trace.CONST`, and `trace.event("...")`.
179    /// - Any non system event ID is forwarded to the host's regular event handler.
180    ///
181    /// This operation does not change the state of the user stack aside from reading the value.
182    Emit = opcodes::EMIT,
183
184    // ----- field operations --------------------------------------------------------------------
185    /// Pops two elements off the stack, adds them, and pushes the result back onto the stack.
186    Add = opcodes::ADD,
187
188    /// Pops an element off the stack, negates it, and pushes the result back onto the stack.
189    Neg = opcodes::NEG,
190
191    /// Pops two elements off the stack, multiplies them, and pushes the result back onto the
192    /// stack.
193    Mul = opcodes::MUL,
194
195    /// Pops an element off the stack, computes its multiplicative inverse, and pushes the result
196    /// back onto the stack.
197    Inv = opcodes::INV,
198
199    /// Pops an element off the stack, adds 1 to it, and pushes the result back onto the stack.
200    Incr = opcodes::INCR,
201
202    /// Pops two elements off the stack, multiplies them, and pushes the result back onto the
203    /// stack.
204    ///
205    /// If either of the elements is greater than 1, execution fails. This operation is equivalent
206    /// to boolean AND.
207    And = opcodes::AND,
208
209    /// Pops two elements off the stack and subtracts their product from their sum.
210    ///
211    /// If either of the elements is greater than 1, execution fails. This operation is equivalent
212    /// to boolean OR.
213    Or = opcodes::OR,
214
215    /// Pops an element off the stack and subtracts it from 1.
216    ///
217    /// If the element is greater than one, the execution fails. This operation is equivalent to
218    /// boolean NOT.
219    Not = opcodes::NOT,
220
221    /// Pops two elements off the stack and compares them. If the elements are equal, pushes 1
222    /// onto the stack, otherwise pushes 0 onto the stack.
223    Eq = opcodes::EQ,
224
225    /// Pops an element off the stack and compares it to 0. If the element is 0, pushes 1 onto
226    /// the stack, otherwise pushes 0 onto the stack.
227    Eqz = opcodes::EQZ,
228
229    /// Computes a single turn of exponent accumulation for the given inputs. This operation can be
230    /// be used to compute a single turn of power of a field element.
231    ///
232    /// The top 4 elements of the stack are expected to be arranged as follows (form the top):
233    /// - least significant bit of the exponent in the previous trace if there's an expacc call,
234    ///   otherwise ZERO
235    /// - exponent of base number `a` for this turn
236    /// - accumulated power of base number `a` so far
237    /// - number which needs to be shifted to the right
238    ///
239    /// At the end of the operation, exponent is replaced with its square, current value of power
240    /// of base number `a` on exponent is incorporated into the accumulator and the number is
241    /// shifted to the right by one bit.
242    Expacc = opcodes::EXPACC,
243
244    // ----- ext2 operations ---------------------------------------------------------------------
245    /// Computes the product of two elements in the extension field of degree 2 and pushes the
246    /// result back onto the stack as the third and fourth elements. Pushes 0 onto the stack as
247    /// the first and second elements.
248    ///
249    /// The extension field is defined as 𝔽ₚ\[x\]/(x² - 7), i.e. using the
250    /// irreducible quadratic polynomial x² - 7 over the base field.
251    Ext2Mul = opcodes::EXT2MUL,
252
253    // ----- u32 operations ----------------------------------------------------------------------
254    /// Pops an element off the stack, splits it into upper and lower 32-bit values, and pushes
255    /// these values back onto the stack.
256    U32split = opcodes::U32SPLIT,
257
258    /// Pops two elements off the stack, adds them, and splits the result into upper and lower
259    /// 32-bit values. Then pushes these values back onto the stack.
260    ///
261    /// If either of these elements is greater than or equal to 2^32, the result of this
262    /// operation is undefined.
263    U32add = opcodes::U32ADD,
264
265    /// Pops two elements off the stack and checks if each of them represents a 32-bit value.
266    /// If both of them are, they are pushed back onto the stack, otherwise an error is returned.
267    ///
268    /// The internal value specifies an error code associated with the error in case when the
269    /// assertion fails.
270    U32assert2(Felt) = opcodes::U32ASSERT2,
271
272    /// Pops three elements off the stack, adds them together, and splits the result into upper
273    /// and lower 32-bit values. Then pushes the result back onto the stack.
274    U32add3 = opcodes::U32ADD3,
275
276    /// Pops two elements off the stack and subtracts the first element from the second. Then,
277    /// the result, together with a flag indicating whether subtraction underflowed is pushed
278    /// onto the stack.
279    ///
280    /// If their of the values is greater than or equal to 2^32, the result of this operation is
281    /// undefined.
282    U32sub = opcodes::U32SUB,
283
284    /// Pops two elements off the stack, multiplies them, and splits the result into upper and
285    /// lower 32-bit values. Then pushes these values back onto the stack.
286    ///
287    /// If their of the values is greater than or equal to 2^32, the result of this operation is
288    /// undefined.
289    U32mul = opcodes::U32MUL,
290
291    /// Pops two elements off the stack and multiplies them. Then pops the third element off the
292    /// stack, and adds it to the result. Finally, splits the result into upper and lower 32-bit
293    /// values, and pushes them onto the stack.
294    ///
295    /// If any of the three values is greater than or equal to 2^32, the result of this operation
296    /// is undefined.
297    U32madd = opcodes::U32MADD,
298
299    /// Pops two elements off the stack and divides the second element by the first. Then pushes
300    /// the integer result of the division, together with the remainder, onto the stack.
301    ///
302    /// If their of the values is greater than or equal to 2^32, the result of this operation is
303    /// undefined.
304    U32div = opcodes::U32DIV,
305
306    /// Pops two elements off the stack, computes their binary AND, and pushes the result back
307    /// onto the stack.
308    ///
309    /// If either of the elements is greater than or equal to 2^32, execution fails.
310    U32and = opcodes::U32AND,
311
312    /// Pops two elements off the stack, computes their binary XOR, and pushes the result back
313    /// onto the stack.
314    ///
315    /// If either of the elements is greater than or equal to 2^32, execution fails.
316    U32xor = opcodes::U32XOR,
317
318    // ----- stack manipulation ------------------------------------------------------------------
319    /// Pushes 0 onto the stack.
320    Pad = opcodes::PAD,
321
322    /// Removes to element from the stack.
323    Drop = opcodes::DROP,
324
325    /// Pushes a copy of stack element 0 onto the stack.
326    Dup0 = opcodes::DUP0,
327
328    /// Pushes a copy of stack element 1 onto the stack.
329    Dup1 = opcodes::DUP1,
330
331    /// Pushes a copy of stack element 2 onto the stack.
332    Dup2 = opcodes::DUP2,
333
334    /// Pushes a copy of stack element 3 onto the stack.
335    Dup3 = opcodes::DUP3,
336
337    /// Pushes a copy of stack element 4 onto the stack.
338    Dup4 = opcodes::DUP4,
339
340    /// Pushes a copy of stack element 5 onto the stack.
341    Dup5 = opcodes::DUP5,
342
343    /// Pushes a copy of stack element 6 onto the stack.
344    Dup6 = opcodes::DUP6,
345
346    /// Pushes a copy of stack element 7 onto the stack.
347    Dup7 = opcodes::DUP7,
348
349    /// Pushes a copy of stack element 9 onto the stack.
350    Dup9 = opcodes::DUP9,
351
352    /// Pushes a copy of stack element 11 onto the stack.
353    Dup11 = opcodes::DUP11,
354
355    /// Pushes a copy of stack element 13 onto the stack.
356    Dup13 = opcodes::DUP13,
357
358    /// Pushes a copy of stack element 15 onto the stack.
359    Dup15 = opcodes::DUP15,
360
361    /// Swaps stack elements 0 and 1.
362    Swap = opcodes::SWAP,
363
364    /// Swaps stack elements 0, 1, 2, and 3 with elements 4, 5, 6, and 7.
365    SwapW = opcodes::SWAPW,
366
367    /// Swaps stack elements 0, 1, 2, and 3 with elements 8, 9, 10, and 11.
368    SwapW2 = opcodes::SWAPW2,
369
370    /// Swaps stack elements 0, 1, 2, and 3, with elements 12, 13, 14, and 15.
371    SwapW3 = opcodes::SWAPW3,
372
373    /// Swaps the top two words pair wise.
374    ///
375    /// Input: [D, C, B, A, ...]
376    /// Output: [B, A, D, C, ...]
377    SwapDW = opcodes::SWAPDW,
378
379    /// Moves stack element 2 to the top of the stack.
380    MovUp2 = opcodes::MOVUP2,
381
382    /// Moves stack element 3 to the top of the stack.
383    MovUp3 = opcodes::MOVUP3,
384
385    /// Moves stack element 4 to the top of the stack.
386    MovUp4 = opcodes::MOVUP4,
387
388    /// Moves stack element 5 to the top of the stack.
389    MovUp5 = opcodes::MOVUP5,
390
391    /// Moves stack element 6 to the top of the stack.
392    MovUp6 = opcodes::MOVUP6,
393
394    /// Moves stack element 7 to the top of the stack.
395    MovUp7 = opcodes::MOVUP7,
396
397    /// Moves stack element 8 to the top of the stack.
398    MovUp8 = opcodes::MOVUP8,
399
400    /// Moves the top stack element to position 2 on the stack.
401    MovDn2 = opcodes::MOVDN2,
402
403    /// Moves the top stack element to position 3 on the stack.
404    MovDn3 = opcodes::MOVDN3,
405
406    /// Moves the top stack element to position 4 on the stack.
407    MovDn4 = opcodes::MOVDN4,
408
409    /// Moves the top stack element to position 5 on the stack.
410    MovDn5 = opcodes::MOVDN5,
411
412    /// Moves the top stack element to position 6 on the stack.
413    MovDn6 = opcodes::MOVDN6,
414
415    /// Moves the top stack element to position 7 on the stack.
416    MovDn7 = opcodes::MOVDN7,
417
418    /// Moves the top stack element to position 8 on the stack.
419    MovDn8 = opcodes::MOVDN8,
420
421    /// Pops an element off the stack, and if the element is 1, swaps the top two remaining
422    /// elements on the stack. If the popped element is 0, the stack remains unchanged.
423    ///
424    /// If the popped element is neither 0 nor 1, execution fails.
425    CSwap = opcodes::CSWAP,
426
427    /// Pops an element off the stack, and if the element is 1, swaps the remaining elements
428    /// 0, 1, 2, and 3 with elements 4, 5, 6, and 7. If the popped element is 0, the stack
429    /// remains unchanged.
430    ///
431    /// If the popped element is neither 0 nor 1, execution fails.
432    CSwapW = opcodes::CSWAPW,
433
434    // ----- input / output ----------------------------------------------------------------------
435    /// Pushes the immediate value onto the stack.
436    Push(Felt) = opcodes::PUSH,
437
438    /// Removes the next element from the advice stack and pushes it onto the operand stack.
439    AdvPop = opcodes::ADVPOP,
440
441    /// Removes a word (4 elements) from the advice stack and overwrites the top four operand
442    /// stack elements with it.
443    AdvPopW = opcodes::ADVPOPW,
444
445    /// Pops an element off the stack, interprets it as a memory address, and replaces the
446    /// remaining 4 elements at the top of the stack with values located at the specified address.
447    MLoadW = opcodes::MLOADW,
448
449    /// Pops an element off the stack, interprets it as a memory address, and writes the remaining
450    /// 4 elements at the top of the stack into memory at the specified address.
451    MStoreW = opcodes::MSTOREW,
452
453    /// Pops an element off the stack, interprets it as a memory address, and pushes the first
454    /// element of the word located at the specified address to the stack.
455    MLoad = opcodes::MLOAD,
456
457    /// Pops an element off the stack, interprets it as a memory address, and writes the remaining
458    /// element at the top of the stack into the first element of the word located at the specified
459    /// memory address. The remaining 3 elements of the word are not affected.
460    MStore = opcodes::MSTORE,
461
462    /// Loads two words from memory, and replaces the top 8 elements of the stack with them,
463    /// element-wise, in stack order.
464    ///
465    /// The operation works as follows:
466    /// - The memory address of the first word is retrieved from 13th stack element (position 12).
467    /// - Two consecutive words, starting at this address, are loaded from memory.
468    /// - The top 8 elements of the stack are overwritten with these words (element-wise, in stack
469    ///   order).
470    /// - Memory address (in position 12) is incremented by 2.
471    /// - All other stack elements remain the same.
472    MStream = opcodes::MSTREAM,
473
474    /// Pops two words from the advice stack, writes them to memory, and replaces the top 8
475    /// elements of the stack with them, element-wise, in stack order.
476    ///
477    /// The operation works as follows:
478    /// - Two words are popped from the advice stack.
479    /// - The destination memory address for the first word is retrieved from the 13th stack element
480    ///   (position 12).
481    /// - The two words are written to memory consecutively, starting at this address.
482    /// - The top 8 elements of the stack are overwritten with these words (element-wise, in stack
483    ///   order).
484    /// - Memory address (in position 12) is incremented by 2.
485    /// - All other stack elements remain the same.
486    Pipe = opcodes::PIPE,
487
488    /// Encrypts two memory words with an Eidos XOF keystream.
489    ///
490    /// Stack transition:
491    /// ```text
492    /// [K_CTR(4), counter, src, dst, remaining, ...]
493    ///     ↓
494    /// [K_CTR(4), counter+1, src+8, dst+16, remaining-1, ...]
495    /// ```
496    CryptoStream = opcodes::CRYPTOSTREAM,
497
498    // ----- cryptographic operations ------------------------------------------------------------
499    /// Performs a block-preserving Eidos compression on the top 3 words of the operand
500    /// stack.
501    ///
502    /// Stack transition:
503    /// [block(8), cv(4), ...] -> [block(8), cv'(4), ...]
504    Compress = opcodes::COMPRESS,
505
506    /// Verifies that a Merkle path from the specified node resolves to the specified root. This
507    /// operation can be used to prove that the prover knows a path in the specified Merkle tree
508    /// which starts with the specified node.
509    ///
510    /// The stack is expected to be arranged as follows (from the top):
511    /// - value of the node, 4 elements.
512    /// - depth of the path, 1 element.
513    /// - index of the node, 1 element.
514    /// - root of the tree, 4 elements.
515    ///
516    /// The Merkle path itself is expected to be provided by the prover non-deterministically (via
517    /// merkle sets). If the prover is not able to provide the required path, the operation fails.
518    /// The state of the stack does not change.
519    ///
520    /// The internal value specifies an error code associated with the error in case when the
521    /// assertion fails.
522    MpVerify(Felt) = opcodes::MPVERIFY,
523
524    /// Computes a new root of a Merkle tree where a node at the specified position is updated to
525    /// the specified value.
526    ///
527    /// The stack is expected to be arranged as follows (from the top):
528    /// - old value of the node, 4 element
529    /// - depth of the node, 1 element
530    /// - index of the node, 1 element
531    /// - current root of the tree, 4 elements
532    /// - new value of the node, 4 element
533    ///
534    /// The Merkle path for the node is expected to be provided by the prover non-deterministically
535    /// via the advice provider. At the end of the operation, the old node value is replaced with
536    /// the new root value, that is computed based on the provided path. Everything else on the
537    /// stack remains the same.
538    ///
539    /// The tree will always be copied into a new instance, meaning the advice provider will keep
540    /// track of both the old and new Merkle trees.
541    MrUpdate = opcodes::MRUPDATE,
542
543    /// Performs FRI (Fast Reed-Solomon Interactive Oracle Proofs) layer folding by a factor of 4
544    /// for FRI protocol executed in a degree 2 extension of the base field.
545    ///
546    /// This operation:
547    /// - Folds 4 query values (v0, v1), (v2, v3), (v4, v5), (v6, v7) into a single value (ne0, ne1)
548    /// - Computes new value of the domain generator power: poe' = poe^4
549    /// - Increments layer pointer (cptr) by 8
550    /// - Checks that the previous folding was done correctly
551    /// - Shifts the stack to move an item from the overflow table to stack position 15
552    ///
553    /// Stack transition:
554    /// Input: [v7, v6, v5, v4, v3, v2, v1, v0, f_pos, coset, poe, pe1, pe0, a1, a0, cptr, ...]
555    /// Output: [t1, t0, s1, s0, df3, df2, df1, df0, poe^2, f_tau, cptr+8, f_pos, poe^4, ne1, ne0,
556    /// eptr, ...] where eptr is moved from the stack overflow table and is the address of the
557    /// final FRI layer.
558    FriE2F4 = opcodes::FRIE2F4,
559
560    /// Performs 8 steps of the Horner evaluation method on a polynomial with coefficients over
561    /// the base field, i.e., it computes
562    ///
563    /// acc' = (((acc_tmp * alpha + c3) * alpha + c2) * alpha + c1) * alpha + c0
564    ///
565    /// where
566    ///
567    /// acc_tmp := (((acc * alpha + c7) * alpha + c6) * alpha + c5) * alpha + c4
568    ///
569    ///
570    /// In other words, the intsruction computes the evaluation at alpha of the polynomial
571    ///
572    /// P(X) := c7 * X^7 + c6 * X^6 + ... + c1 * X + c0
573    HornerBase = opcodes::HORNERBASE,
574
575    /// Performs 4 steps of the Horner evaluation method on a polynomial with coefficients over
576    /// the extension field, i.e., it computes
577    ///
578    /// acc' = (((acc * alpha + c3) * alpha + c2) * alpha + c1) * alpha + c0
579    ///
580    /// In other words, the intsruction computes the evaluation at alpha of the polynomial
581    ///
582    /// P(X) := c3 * X^3 + c2 * X^2 + c1 * X + c0
583    HornerExt = opcodes::HORNEREXT,
584
585    /// Evaluates an arithmetic circuit given a pointer to its description in memory, the number
586    /// of arithmetic gates, and the sum of the input and constant gates.
587    EvalCircuit = opcodes::EVALCIRCUIT,
588
589    /// Logs a precompile event. This instruction is used to signal that a precompile computation
590    /// was requested.
591    LogDeferred = opcodes::LOGDEFERRED,
592}
593
594impl Operation {
595    pub const OP_BITS: usize = 7;
596
597    /// Returns the opcode of this operation.
598    #[rustfmt::skip]
599    pub fn op_code(&self) -> u8 {
600        // SAFETY: This is safe because we have given this enum a primitive representation with
601        // #[repr(u8)], with the first field of the underlying union-of-structs the discriminant.
602        //
603        // See the section on "accessing the numeric value of the discriminant"
604        // here: https://doc.rust-lang.org/std/mem/fn.discriminant.html
605        unsafe { *<*const _>::from(self).cast::<u8>() }
606    }
607
608    /// Returns an immediate value carried by this operation.
609    // Proptest generators for operations in crate::mast::node::basic_block_node::tests discriminate
610    // on this flag, please update them when you modify the semantics of this method.
611    pub fn imm_value(&self) -> Option<Felt> {
612        match *self {
613            Self::Push(imm) => Some(imm),
614            _ => None,
615        }
616    }
617
618    /// Returns true if this basic block operation increases the stack depth by one.
619    ///
620    /// Note: this only applies to operations within basic blocks (i.e. those executed via
621    /// `ResumeBasicBlock` continuations). Control flow operations that affect stack size
622    /// (e.g. Split, Loop, Dyn) are handled separately.
623    pub fn increments_stack_size(&self) -> bool {
624        matches!(
625            self,
626            Self::Push(_)
627                | Self::Pad
628                | Self::Dup0
629                | Self::Dup1
630                | Self::Dup2
631                | Self::Dup3
632                | Self::Dup4
633                | Self::Dup5
634                | Self::Dup6
635                | Self::Dup7
636                | Self::Dup9
637                | Self::Dup11
638                | Self::Dup13
639                | Self::Dup15
640                | Self::U32split
641                | Self::SDepth
642                | Self::Clk
643                | Self::AdvPop
644        )
645    }
646
647    /// Returns true if this basic block operation decreases the stack depth by one.
648    ///
649    /// Note: this only applies to operations within basic blocks (i.e. those executed via
650    /// `ResumeBasicBlock` continuations). Control flow operations that affect stack size
651    /// (e.g. Split, Loop, Dyn) are handled separately.
652    pub fn decrements_stack_size(&self) -> bool {
653        matches!(
654            self,
655            Self::Drop
656                | Self::Assert(_)
657                | Self::Add
658                | Self::Mul
659                | Self::And
660                | Self::Or
661                | Self::Eq
662                | Self::U32add3
663                | Self::U32madd
664                | Self::U32and
665                | Self::U32xor
666                | Self::CSwap
667                | Self::CSwapW
668                | Self::MLoadW
669                | Self::MStoreW
670                | Self::MStore
671                | Self::FriE2F4
672        )
673    }
674}
675
676impl crate::prettier::PrettyPrint for Operation {
677    fn render(&self) -> crate::prettier::Document {
678        crate::prettier::display(self)
679    }
680}
681
682impl fmt::Display for Operation {
683    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
684        match self {
685            // ----- system operations ------------------------------------------------------------
686            Self::Noop => write!(f, "noop"),
687            Self::Assert(err_code) => write!(f, "assert({err_code})"),
688
689            Self::SDepth => write!(f, "sdepth"),
690            Self::Caller => write!(f, "caller"),
691
692            Self::Clk => write!(f, "clk"),
693
694            // ----- field operations -------------------------------------------------------------
695            Self::Add => write!(f, "add"),
696            Self::Neg => write!(f, "neg"),
697            Self::Mul => write!(f, "mul"),
698            Self::Inv => write!(f, "inv"),
699            Self::Incr => write!(f, "incr"),
700
701            Self::And => write!(f, "and"),
702            Self::Or => write!(f, "or"),
703            Self::Not => write!(f, "not"),
704
705            Self::Eq => write!(f, "eq"),
706            Self::Eqz => write!(f, "eqz"),
707
708            Self::Expacc => write!(f, "expacc"),
709
710            // ----- ext2 operations --------------------------------------------------------------
711            Self::Ext2Mul => write!(f, "ext2mul"),
712
713            // ----- u32 operations ---------------------------------------------------------------
714            Self::U32assert2(err_code) => write!(f, "u32assert2({err_code})"),
715            Self::U32split => write!(f, "u32split"),
716            Self::U32add => write!(f, "u32add"),
717            Self::U32add3 => write!(f, "u32add3"),
718            Self::U32sub => write!(f, "u32sub"),
719            Self::U32mul => write!(f, "u32mul"),
720            Self::U32madd => write!(f, "u32madd"),
721            Self::U32div => write!(f, "u32div"),
722
723            Self::U32and => write!(f, "u32and"),
724            Self::U32xor => write!(f, "u32xor"),
725
726            // ----- stack manipulation -----------------------------------------------------------
727            Self::Drop => write!(f, "drop"),
728            Self::Pad => write!(f, "pad"),
729
730            Self::Dup0 => write!(f, "dup0"),
731            Self::Dup1 => write!(f, "dup1"),
732            Self::Dup2 => write!(f, "dup2"),
733            Self::Dup3 => write!(f, "dup3"),
734            Self::Dup4 => write!(f, "dup4"),
735            Self::Dup5 => write!(f, "dup5"),
736            Self::Dup6 => write!(f, "dup6"),
737            Self::Dup7 => write!(f, "dup7"),
738            Self::Dup9 => write!(f, "dup9"),
739            Self::Dup11 => write!(f, "dup11"),
740            Self::Dup13 => write!(f, "dup13"),
741            Self::Dup15 => write!(f, "dup15"),
742
743            Self::Swap => write!(f, "swap"),
744            Self::SwapW => write!(f, "swapw"),
745            Self::SwapW2 => write!(f, "swapw2"),
746            Self::SwapW3 => write!(f, "swapw3"),
747            Self::SwapDW => write!(f, "swapdw"),
748
749            Self::MovUp2 => write!(f, "movup2"),
750            Self::MovUp3 => write!(f, "movup3"),
751            Self::MovUp4 => write!(f, "movup4"),
752            Self::MovUp5 => write!(f, "movup5"),
753            Self::MovUp6 => write!(f, "movup6"),
754            Self::MovUp7 => write!(f, "movup7"),
755            Self::MovUp8 => write!(f, "movup8"),
756
757            Self::MovDn2 => write!(f, "movdn2"),
758            Self::MovDn3 => write!(f, "movdn3"),
759            Self::MovDn4 => write!(f, "movdn4"),
760            Self::MovDn5 => write!(f, "movdn5"),
761            Self::MovDn6 => write!(f, "movdn6"),
762            Self::MovDn7 => write!(f, "movdn7"),
763            Self::MovDn8 => write!(f, "movdn8"),
764
765            Self::CSwap => write!(f, "cswap"),
766            Self::CSwapW => write!(f, "cswapw"),
767
768            // ----- input / output ---------------------------------------------------------------
769            Self::Push(value) => write!(f, "push({value})"),
770
771            Self::AdvPop => write!(f, "advpop"),
772            Self::AdvPopW => write!(f, "advpopw"),
773
774            Self::MLoadW => write!(f, "mloadw"),
775            Self::MStoreW => write!(f, "mstorew"),
776
777            Self::MLoad => write!(f, "mload"),
778            Self::MStore => write!(f, "mstore"),
779
780            Self::MStream => write!(f, "mstream"),
781            Self::Pipe => write!(f, "pipe"),
782            Self::CryptoStream => write!(f, "crypto_stream"),
783
784            Self::Emit => write!(f, "emit"),
785
786            // ----- cryptographic operations -----------------------------------------------------
787            Self::Compress => write!(f, "compress"),
788            Self::MpVerify(err_code) => write!(f, "mpverify({err_code})"),
789            Self::MrUpdate => write!(f, "mrupdate"),
790
791            // ----- STARK proof verification -----------------------------------------------------
792            Self::FriE2F4 => write!(f, "frie2f4"),
793            Self::HornerBase => write!(f, "horner_eval_base"),
794            Self::HornerExt => write!(f, "horner_eval_ext"),
795            Self::EvalCircuit => write!(f, "eval_circuit"),
796            Self::LogDeferred => write!(f, "log_deferred"),
797        }
798    }
799}
800
801// ARBITRARY (proptest)
802// ================================================================================================
803
804#[cfg(any(test, feature = "arbitrary"))]
805mod operation_arbitrary {
806    use proptest::prelude::*;
807
808    use super::Operation;
809    use crate::Felt;
810
811    impl Arbitrary for Operation {
812        type Parameters = ();
813        type Strategy = BoxedStrategy<Self>;
814
815        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
816            // All 81 variants are any-valid on the wire: unit variants carry only the op code,
817            // and the four data-carrying variants (Assert, U32assert2, Push, MpVerify) take an
818            // arbitrary Felt. prop_oneof exercises every discriminant in both directions.
819            prop_oneof![
820                any::<Felt>().prop_map(Self::Assert).boxed(),
821                any::<Felt>().prop_map(Self::U32assert2).boxed(),
822                any::<Felt>().prop_map(Self::Push).boxed(),
823                any::<Felt>().prop_map(Self::MpVerify).boxed(),
824                Just(Self::Noop),
825                Just(Self::SDepth),
826                Just(Self::Caller),
827                Just(Self::Clk),
828                Just(Self::Emit),
829                Just(Self::Add),
830                Just(Self::Neg),
831                Just(Self::Mul),
832                Just(Self::Inv),
833                Just(Self::Incr),
834                Just(Self::And),
835                Just(Self::Or),
836                Just(Self::Not),
837                Just(Self::Eq),
838                Just(Self::Eqz),
839                Just(Self::Expacc),
840                Just(Self::Ext2Mul),
841                Just(Self::U32split),
842                Just(Self::U32add),
843                Just(Self::U32add3),
844                Just(Self::U32sub),
845                Just(Self::U32mul),
846                Just(Self::U32madd),
847                Just(Self::U32div),
848                Just(Self::U32and),
849                Just(Self::U32xor),
850                Just(Self::Pad),
851                Just(Self::Drop),
852                Just(Self::Dup0),
853                Just(Self::Dup1),
854                Just(Self::Dup2),
855                Just(Self::Dup3),
856                Just(Self::Dup4),
857                Just(Self::Dup5),
858                Just(Self::Dup6),
859                Just(Self::Dup7),
860                Just(Self::Dup9),
861                Just(Self::Dup11),
862                Just(Self::Dup13),
863                Just(Self::Dup15),
864                Just(Self::Swap),
865                Just(Self::SwapW),
866                Just(Self::SwapW2),
867                Just(Self::SwapW3),
868                Just(Self::SwapDW),
869                Just(Self::MovUp2),
870                Just(Self::MovUp3),
871                Just(Self::MovUp4),
872                Just(Self::MovUp5),
873                Just(Self::MovUp6),
874                Just(Self::MovUp7),
875                Just(Self::MovUp8),
876                Just(Self::MovDn2),
877                Just(Self::MovDn3),
878                Just(Self::MovDn4),
879                Just(Self::MovDn5),
880                Just(Self::MovDn6),
881                Just(Self::MovDn7),
882                Just(Self::MovDn8),
883                Just(Self::CSwap),
884                Just(Self::CSwapW),
885                Just(Self::AdvPop),
886                Just(Self::AdvPopW),
887                Just(Self::MLoadW),
888                Just(Self::MStoreW),
889                Just(Self::MLoad),
890                Just(Self::MStore),
891                Just(Self::MStream),
892                Just(Self::Pipe),
893                Just(Self::CryptoStream),
894                Just(Self::Compress),
895                Just(Self::MrUpdate),
896                Just(Self::FriE2F4),
897                Just(Self::HornerBase),
898                Just(Self::HornerExt),
899                Just(Self::EvalCircuit),
900                Just(Self::LogDeferred),
901            ]
902            .boxed()
903        }
904    }
905}
906
907impl Serializable for Operation {
908    fn write_into<W: ByteWriter>(&self, target: &mut W) {
909        target.write_u8(self.op_code());
910
911        // For operations that have extra data, encode it in `data`.
912        match self {
913            Operation::Assert(err_code)
914            | Operation::MpVerify(err_code)
915            | Operation::U32assert2(err_code) => {
916                err_code.write_into(target);
917            },
918            Operation::Push(value) => value.as_canonical_u64().write_into(target),
919
920            // Note: we explicitly write out all the operations so that whenever we make a
921            // modification to the `Operation` enum, we get a compile error here. This
922            // should help us remember to properly encode/decode each operation variant.
923            Operation::Noop
924            | Operation::SDepth
925            | Operation::Caller
926            | Operation::Clk
927            | Operation::Add
928            | Operation::Neg
929            | Operation::Mul
930            | Operation::Inv
931            | Operation::Incr
932            | Operation::And
933            | Operation::Or
934            | Operation::Not
935            | Operation::Eq
936            | Operation::Eqz
937            | Operation::Expacc
938            | Operation::Ext2Mul
939            | Operation::U32split
940            | Operation::U32add
941            | Operation::U32add3
942            | Operation::U32sub
943            | Operation::U32mul
944            | Operation::U32madd
945            | Operation::U32div
946            | Operation::U32and
947            | Operation::U32xor
948            | Operation::Pad
949            | Operation::Drop
950            | Operation::Dup0
951            | Operation::Dup1
952            | Operation::Dup2
953            | Operation::Dup3
954            | Operation::Dup4
955            | Operation::Dup5
956            | Operation::Dup6
957            | Operation::Dup7
958            | Operation::Dup9
959            | Operation::Dup11
960            | Operation::Dup13
961            | Operation::Dup15
962            | Operation::Swap
963            | Operation::SwapW
964            | Operation::SwapW2
965            | Operation::SwapW3
966            | Operation::SwapDW
967            | Operation::Emit
968            | Operation::MovUp2
969            | Operation::MovUp3
970            | Operation::MovUp4
971            | Operation::MovUp5
972            | Operation::MovUp6
973            | Operation::MovUp7
974            | Operation::MovUp8
975            | Operation::MovDn2
976            | Operation::MovDn3
977            | Operation::MovDn4
978            | Operation::MovDn5
979            | Operation::MovDn6
980            | Operation::MovDn7
981            | Operation::MovDn8
982            | Operation::CSwap
983            | Operation::CSwapW
984            | Operation::AdvPop
985            | Operation::AdvPopW
986            | Operation::MLoadW
987            | Operation::MStoreW
988            | Operation::MLoad
989            | Operation::MStore
990            | Operation::MStream
991            | Operation::Pipe
992            | Operation::CryptoStream
993            | Operation::Compress
994            | Operation::MrUpdate
995            | Operation::FriE2F4
996            | Operation::HornerBase
997            | Operation::HornerExt
998            | Operation::EvalCircuit
999            | Operation::LogDeferred => (),
1000        }
1001    }
1002}
1003
1004impl Operation {
1005    /// Returns the serialized size of this operation in bytes.
1006    pub(crate) fn encoded_size(&self) -> usize {
1007        let mut size = size_of::<u8>();
1008        match self {
1009            Operation::Assert(err_code)
1010            | Operation::MpVerify(err_code)
1011            | Operation::U32assert2(err_code) => {
1012                size += err_code.get_size_hint();
1013            },
1014            Operation::Push(value) => {
1015                size += value.as_canonical_u64().get_size_hint();
1016            },
1017            Operation::Noop
1018            | Operation::SDepth
1019            | Operation::Caller
1020            | Operation::Clk
1021            | Operation::Add
1022            | Operation::Neg
1023            | Operation::Mul
1024            | Operation::Inv
1025            | Operation::Incr
1026            | Operation::And
1027            | Operation::Or
1028            | Operation::Not
1029            | Operation::Eq
1030            | Operation::Eqz
1031            | Operation::Expacc
1032            | Operation::Ext2Mul
1033            | Operation::U32split
1034            | Operation::U32add
1035            | Operation::U32add3
1036            | Operation::U32sub
1037            | Operation::U32mul
1038            | Operation::U32madd
1039            | Operation::U32div
1040            | Operation::U32and
1041            | Operation::U32xor
1042            | Operation::Pad
1043            | Operation::Drop
1044            | Operation::Dup0
1045            | Operation::Dup1
1046            | Operation::Dup2
1047            | Operation::Dup3
1048            | Operation::Dup4
1049            | Operation::Dup5
1050            | Operation::Dup6
1051            | Operation::Dup7
1052            | Operation::Dup9
1053            | Operation::Dup11
1054            | Operation::Dup13
1055            | Operation::Dup15
1056            | Operation::Swap
1057            | Operation::SwapW
1058            | Operation::SwapW2
1059            | Operation::SwapW3
1060            | Operation::SwapDW
1061            | Operation::Emit
1062            | Operation::MovUp2
1063            | Operation::MovUp3
1064            | Operation::MovUp4
1065            | Operation::MovUp5
1066            | Operation::MovUp6
1067            | Operation::MovUp7
1068            | Operation::MovUp8
1069            | Operation::MovDn2
1070            | Operation::MovDn3
1071            | Operation::MovDn4
1072            | Operation::MovDn5
1073            | Operation::MovDn6
1074            | Operation::MovDn7
1075            | Operation::MovDn8
1076            | Operation::CSwap
1077            | Operation::CSwapW
1078            | Operation::AdvPop
1079            | Operation::AdvPopW
1080            | Operation::MLoadW
1081            | Operation::MStoreW
1082            | Operation::MLoad
1083            | Operation::MStore
1084            | Operation::MStream
1085            | Operation::Pipe
1086            | Operation::CryptoStream
1087            | Operation::Compress
1088            | Operation::MrUpdate
1089            | Operation::FriE2F4
1090            | Operation::HornerBase
1091            | Operation::HornerExt
1092            | Operation::EvalCircuit
1093            | Operation::LogDeferred => (),
1094        }
1095        size
1096    }
1097}
1098
1099impl Deserializable for Operation {
1100    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
1101        let op_code = source.read_u8()?;
1102
1103        let operation = match op_code {
1104            opcodes::NOOP => Self::Noop,
1105            opcodes::EQZ => Self::Eqz,
1106            opcodes::NEG => Self::Neg,
1107            opcodes::INV => Self::Inv,
1108            opcodes::INCR => Self::Incr,
1109            opcodes::NOT => Self::Not,
1110            opcodes::MLOAD => Self::MLoad,
1111            opcodes::SWAP => Self::Swap,
1112            opcodes::CALLER => Self::Caller,
1113            opcodes::MOVUP2 => Self::MovUp2,
1114            opcodes::MOVDN2 => Self::MovDn2,
1115            opcodes::MOVUP3 => Self::MovUp3,
1116            opcodes::MOVDN3 => Self::MovDn3,
1117            opcodes::ADVPOPW => Self::AdvPopW,
1118            opcodes::EXPACC => Self::Expacc,
1119
1120            opcodes::MOVUP4 => Self::MovUp4,
1121            opcodes::MOVDN4 => Self::MovDn4,
1122            opcodes::MOVUP5 => Self::MovUp5,
1123            opcodes::MOVDN5 => Self::MovDn5,
1124            opcodes::MOVUP6 => Self::MovUp6,
1125            opcodes::MOVDN6 => Self::MovDn6,
1126            opcodes::MOVUP7 => Self::MovUp7,
1127            opcodes::MOVDN7 => Self::MovDn7,
1128            opcodes::SWAPW => Self::SwapW,
1129            opcodes::EXT2MUL => Self::Ext2Mul,
1130            opcodes::MOVUP8 => Self::MovUp8,
1131            opcodes::MOVDN8 => Self::MovDn8,
1132            opcodes::SWAPW2 => Self::SwapW2,
1133            opcodes::SWAPW3 => Self::SwapW3,
1134            opcodes::SWAPDW => Self::SwapDW,
1135            opcodes::EMIT => Self::Emit,
1136
1137            opcodes::ASSERT => Self::Assert(Felt::read_from(source)?),
1138            opcodes::EQ => Self::Eq,
1139            opcodes::ADD => Self::Add,
1140            opcodes::MUL => Self::Mul,
1141            opcodes::AND => Self::And,
1142            opcodes::OR => Self::Or,
1143            opcodes::U32AND => Self::U32and,
1144            opcodes::U32XOR => Self::U32xor,
1145            opcodes::FRIE2F4 => Self::FriE2F4,
1146            opcodes::DROP => Self::Drop,
1147            opcodes::CSWAP => Self::CSwap,
1148            opcodes::CSWAPW => Self::CSwapW,
1149            opcodes::MLOADW => Self::MLoadW,
1150            opcodes::MSTORE => Self::MStore,
1151            opcodes::MSTOREW => Self::MStoreW,
1152
1153            opcodes::PAD => Self::Pad,
1154            opcodes::DUP0 => Self::Dup0,
1155            opcodes::DUP1 => Self::Dup1,
1156            opcodes::DUP2 => Self::Dup2,
1157            opcodes::DUP3 => Self::Dup3,
1158            opcodes::DUP4 => Self::Dup4,
1159            opcodes::DUP5 => Self::Dup5,
1160            opcodes::DUP6 => Self::Dup6,
1161            opcodes::DUP7 => Self::Dup7,
1162            opcodes::DUP9 => Self::Dup9,
1163            opcodes::DUP11 => Self::Dup11,
1164            opcodes::DUP13 => Self::Dup13,
1165            opcodes::DUP15 => Self::Dup15,
1166            opcodes::ADVPOP => Self::AdvPop,
1167            opcodes::SDEPTH => Self::SDepth,
1168            opcodes::CLK => Self::Clk,
1169
1170            opcodes::U32ADD => Self::U32add,
1171            opcodes::U32SUB => Self::U32sub,
1172            opcodes::U32MUL => Self::U32mul,
1173            opcodes::U32DIV => Self::U32div,
1174            opcodes::U32SPLIT => Self::U32split,
1175            opcodes::U32ASSERT2 => Self::U32assert2(Felt::read_from(source)?),
1176            opcodes::U32ADD3 => Self::U32add3,
1177            opcodes::U32MADD => Self::U32madd,
1178
1179            opcodes::COMPRESS => Self::Compress,
1180            opcodes::MPVERIFY => Self::MpVerify(Felt::read_from(source)?),
1181            opcodes::PIPE => Self::Pipe,
1182            opcodes::MSTREAM => Self::MStream,
1183            opcodes::CRYPTOSTREAM => Self::CryptoStream,
1184            opcodes::HORNERBASE => Self::HornerBase,
1185            opcodes::HORNEREXT => Self::HornerExt,
1186            opcodes::LOGDEFERRED => Self::LogDeferred,
1187            opcodes::EVALCIRCUIT => Self::EvalCircuit,
1188
1189            opcodes::MRUPDATE => Self::MrUpdate,
1190            opcodes::PUSH => Self::Push(Felt::read_from(source)?),
1191            _ => {
1192                return Err(DeserializationError::InvalidValue(format!(
1193                    "Invalid opcode '{op_code}'"
1194                )));
1195            },
1196        };
1197
1198        Ok(operation)
1199    }
1200
1201    /// Returns the minimum serialized size: 1 byte opcode.
1202    ///
1203    /// Some operations have additional payload (e.g., Push has 8 bytes for Felt),
1204    /// but the minimum is just the opcode byte.
1205    fn min_serialized_size() -> usize {
1206        1
1207    }
1208}
1209
1210#[cfg(test)]
1211mod tests;