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;