1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
use super::{
    super::AdviceInjectorNode, debug, ByteReader, CodeBody, Deserializable, DeserializationError,
    Felt, Instruction, Node, OpCode, ProcedureId, RpoDigest, MAX_PUSH_INPUTS,
};
use alloc::string::ToString;

// NODE DESERIALIZATION
// ================================================================================================

impl Deserializable for Node {
    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
        let first_byte = source.peek_u8()?;

        if first_byte == OpCode::IfElse as u8 {
            source.read_u8()?;

            let if_block_len = source.read_u16()? as usize;
            let nodes = source.read_many::<Node>(if_block_len)?;
            let true_case = CodeBody::new(nodes);

            let else_block_len = source.read_u16()? as usize;
            let nodes = source.read_many::<Node>(else_block_len)?;
            let false_case = CodeBody::new(nodes);

            Ok(Node::IfElse {
                true_case,
                false_case,
            })
        } else if first_byte == OpCode::Repeat as u8 {
            source.read_u8()?;

            let times = source.read_u32()?;

            let nodes_len = source.read_u16()? as usize;
            let nodes = source.read_many::<Node>(nodes_len)?;
            let body = CodeBody::new(nodes);

            Ok(Node::Repeat { times, body })
        } else if first_byte == OpCode::While as u8 {
            source.read_u8()?;

            let nodes_len = source.read_u16()? as usize;
            let nodes = source.read_many::<Node>(nodes_len)?;
            let body = CodeBody::new(nodes);

            Ok(Node::While { body })
        } else {
            let inner = Deserializable::read_from(source)?;
            Ok(Node::Instruction(inner))
        }
    }
}

// INSTRUCTION DESERIALIZATION
// ================================================================================================

impl Deserializable for Instruction {
    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
        let opcode = OpCode::read_from(source)?;

        match opcode {
            OpCode::Assert => Ok(Instruction::Assert),
            OpCode::AssertWithError => Ok(Instruction::AssertWithError(source.read_u32()?)),
            OpCode::AssertEq => Ok(Instruction::AssertEq),
            OpCode::AssertEqWithError => Ok(Instruction::AssertEqWithError(source.read_u32()?)),
            OpCode::AssertEqw => Ok(Instruction::AssertEqw),
            OpCode::AssertEqwWithError => Ok(Instruction::AssertEqwWithError(source.read_u32()?)),
            OpCode::Assertz => Ok(Instruction::Assertz),
            OpCode::AssertzWithError => Ok(Instruction::AssertzWithError(source.read_u32()?)),
            OpCode::Add => Ok(Instruction::Add),
            OpCode::AddImm => Ok(Instruction::AddImm(Felt::read_from(source)?)),
            OpCode::Sub => Ok(Instruction::Sub),
            OpCode::SubImm => Ok(Instruction::SubImm(Felt::read_from(source)?)),
            OpCode::Mul => Ok(Instruction::Mul),
            OpCode::MulImm => Ok(Instruction::MulImm(Felt::read_from(source)?)),
            OpCode::Div => Ok(Instruction::Div),
            OpCode::DivImm => Ok(Instruction::DivImm(Felt::read_from(source)?)),
            OpCode::Neg => Ok(Instruction::Neg),
            OpCode::Inv => Ok(Instruction::Inv),
            OpCode::Incr => Ok(Instruction::Incr),
            OpCode::Pow2 => Ok(Instruction::Pow2),
            OpCode::Exp => Ok(Instruction::Exp),
            OpCode::ExpImm => Ok(Instruction::ExpImm(Felt::read_from(source)?)),
            OpCode::ExpBitLength => Ok(Instruction::ExpBitLength(source.read_u8()?)),
            OpCode::ILog2 => Ok(Instruction::ILog2),
            OpCode::Not => Ok(Instruction::Not),
            OpCode::And => Ok(Instruction::And),
            OpCode::Or => Ok(Instruction::Or),
            OpCode::Xor => Ok(Instruction::Xor),
            OpCode::Eq => Ok(Instruction::Eq),
            OpCode::EqImm => Ok(Instruction::EqImm(Felt::read_from(source)?)),
            OpCode::Neq => Ok(Instruction::Neq),
            OpCode::NeqImm => Ok(Instruction::NeqImm(Felt::read_from(source)?)),
            OpCode::Eqw => Ok(Instruction::Eqw),
            OpCode::Lt => Ok(Instruction::Lt),
            OpCode::Lte => Ok(Instruction::Lte),
            OpCode::Gt => Ok(Instruction::Gt),
            OpCode::Gte => Ok(Instruction::Gte),
            OpCode::IsOdd => Ok(Instruction::IsOdd),

            // ----- ext2 operations --------------------------------------------------------------
            OpCode::Ext2Add => Ok(Instruction::Ext2Add),
            OpCode::Ext2Sub => Ok(Instruction::Ext2Sub),
            OpCode::Ext2Mul => Ok(Instruction::Ext2Mul),
            OpCode::Ext2Div => Ok(Instruction::Ext2Div),
            OpCode::Ext2Neg => Ok(Instruction::Ext2Neg),
            OpCode::Ext2Inv => Ok(Instruction::Ext2Inv),

            // ----- u32 manipulation -------------------------------------------------------------
            OpCode::U32Test => Ok(Instruction::U32Test),
            OpCode::U32TestW => Ok(Instruction::U32TestW),
            OpCode::U32Assert => Ok(Instruction::U32Assert),
            OpCode::U32AssertWithError => Ok(Instruction::U32AssertWithError(source.read_u32()?)),
            OpCode::U32Assert2 => Ok(Instruction::U32Assert2),
            OpCode::U32Assert2WithError => Ok(Instruction::U32Assert2WithError(source.read_u32()?)),
            OpCode::U32AssertW => Ok(Instruction::U32AssertW),
            OpCode::U32AssertWWithError => Ok(Instruction::U32AssertWWithError(source.read_u32()?)),
            OpCode::U32Split => Ok(Instruction::U32Split),
            OpCode::U32Cast => Ok(Instruction::U32Cast),
            OpCode::U32WrappingAdd => Ok(Instruction::U32WrappingAdd),
            OpCode::U32WrappingAddImm => Ok(Instruction::U32WrappingAddImm(source.read_u32()?)),
            OpCode::U32OverflowingAdd => Ok(Instruction::U32OverflowingAdd),
            OpCode::U32OverflowingAddImm => {
                Ok(Instruction::U32OverflowingAddImm(source.read_u32()?))
            }
            OpCode::U32OverflowingAdd3 => Ok(Instruction::U32OverflowingAdd3),
            OpCode::U32WrappingAdd3 => Ok(Instruction::U32WrappingAdd3),
            OpCode::U32WrappingSub => Ok(Instruction::U32WrappingSub),
            OpCode::U32WrappingSubImm => Ok(Instruction::U32WrappingSubImm(source.read_u32()?)),
            OpCode::U32OverflowingSub => Ok(Instruction::U32OverflowingSub),
            OpCode::U32OverflowingSubImm => {
                Ok(Instruction::U32OverflowingSubImm(source.read_u32()?))
            }
            OpCode::U32WrappingMul => Ok(Instruction::U32WrappingMul),
            OpCode::U32WrappingMulImm => Ok(Instruction::U32WrappingMulImm(source.read_u32()?)),
            OpCode::U32OverflowingMul => Ok(Instruction::U32OverflowingMul),
            OpCode::U32OverflowingMulImm => {
                Ok(Instruction::U32OverflowingMulImm(source.read_u32()?))
            }
            OpCode::U32OverflowingMadd => Ok(Instruction::U32OverflowingMadd),
            OpCode::U32WrappingMadd => Ok(Instruction::U32WrappingMadd),
            OpCode::U32Div => Ok(Instruction::U32Div),
            OpCode::U32DivImm => Ok(Instruction::U32DivImm(source.read_u32()?)),
            OpCode::U32Mod => Ok(Instruction::U32Mod),
            OpCode::U32ModImm => Ok(Instruction::U32ModImm(source.read_u32()?)),
            OpCode::U32DivMod => Ok(Instruction::U32DivMod),
            OpCode::U32DivModImm => Ok(Instruction::U32DivModImm(source.read_u32()?)),
            OpCode::U32And => Ok(Instruction::U32And),
            OpCode::U32Or => Ok(Instruction::U32Or),
            OpCode::U32Xor => Ok(Instruction::U32Xor),
            OpCode::U32Not => Ok(Instruction::U32Not),
            OpCode::U32Shr => Ok(Instruction::U32Shr),
            OpCode::U32ShrImm => Ok(Instruction::U32ShrImm(source.read_u8()?)),
            OpCode::U32Shl => Ok(Instruction::U32Shl),
            OpCode::U32ShlImm => Ok(Instruction::U32ShlImm(source.read_u8()?)),
            OpCode::U32Rotr => Ok(Instruction::U32Rotr),
            OpCode::U32RotrImm => Ok(Instruction::U32RotrImm(source.read_u8()?)),
            OpCode::U32Rotl => Ok(Instruction::U32Rotl),
            OpCode::U32RotlImm => Ok(Instruction::U32RotlImm(source.read_u8()?)),
            OpCode::U32Popcnt => Ok(Instruction::U32Popcnt),
            OpCode::U32Clz => Ok(Instruction::U32Clz),
            OpCode::U32Ctz => Ok(Instruction::U32Ctz),
            OpCode::U32Clo => Ok(Instruction::U32Clo),
            OpCode::U32Cto => Ok(Instruction::U32Cto),
            OpCode::U32Lt => Ok(Instruction::U32Lt),
            OpCode::U32Lte => Ok(Instruction::U32Lte),
            OpCode::U32Gt => Ok(Instruction::U32Gt),
            OpCode::U32Gte => Ok(Instruction::U32Gte),
            OpCode::U32Min => Ok(Instruction::U32Min),
            OpCode::U32Max => Ok(Instruction::U32Max),

            // ----- stack manipulation -----------------------------------------------------------
            OpCode::Drop => Ok(Instruction::Drop),
            OpCode::DropW => Ok(Instruction::DropW),
            OpCode::PadW => Ok(Instruction::PadW),
            OpCode::Dup0 => Ok(Instruction::Dup0),
            OpCode::Dup1 => Ok(Instruction::Dup1),
            OpCode::Dup2 => Ok(Instruction::Dup2),
            OpCode::Dup3 => Ok(Instruction::Dup3),
            OpCode::Dup4 => Ok(Instruction::Dup4),
            OpCode::Dup5 => Ok(Instruction::Dup5),
            OpCode::Dup6 => Ok(Instruction::Dup6),
            OpCode::Dup7 => Ok(Instruction::Dup7),
            OpCode::Dup8 => Ok(Instruction::Dup8),
            OpCode::Dup9 => Ok(Instruction::Dup9),
            OpCode::Dup10 => Ok(Instruction::Dup10),
            OpCode::Dup11 => Ok(Instruction::Dup11),
            OpCode::Dup12 => Ok(Instruction::Dup12),
            OpCode::Dup13 => Ok(Instruction::Dup13),
            OpCode::Dup14 => Ok(Instruction::Dup14),
            OpCode::Dup15 => Ok(Instruction::Dup15),
            OpCode::DupW0 => Ok(Instruction::DupW0),
            OpCode::DupW1 => Ok(Instruction::DupW1),
            OpCode::DupW2 => Ok(Instruction::DupW2),
            OpCode::DupW3 => Ok(Instruction::DupW3),
            OpCode::Swap1 => Ok(Instruction::Swap1),
            OpCode::Swap2 => Ok(Instruction::Swap2),
            OpCode::Swap3 => Ok(Instruction::Swap3),
            OpCode::Swap4 => Ok(Instruction::Swap4),
            OpCode::Swap5 => Ok(Instruction::Swap5),
            OpCode::Swap6 => Ok(Instruction::Swap6),
            OpCode::Swap7 => Ok(Instruction::Swap7),
            OpCode::Swap8 => Ok(Instruction::Swap8),
            OpCode::Swap9 => Ok(Instruction::Swap9),
            OpCode::Swap10 => Ok(Instruction::Swap10),
            OpCode::Swap11 => Ok(Instruction::Swap11),
            OpCode::Swap12 => Ok(Instruction::Swap12),
            OpCode::Swap13 => Ok(Instruction::Swap13),
            OpCode::Swap14 => Ok(Instruction::Swap14),
            OpCode::Swap15 => Ok(Instruction::Swap15),
            OpCode::SwapW1 => Ok(Instruction::SwapW1),
            OpCode::SwapW2 => Ok(Instruction::SwapW2),
            OpCode::SwapW3 => Ok(Instruction::SwapW3),
            OpCode::SwapDW => Ok(Instruction::SwapDw),
            OpCode::MovUp2 => Ok(Instruction::MovUp2),
            OpCode::MovUp3 => Ok(Instruction::MovUp3),
            OpCode::MovUp4 => Ok(Instruction::MovUp4),
            OpCode::MovUp5 => Ok(Instruction::MovUp5),
            OpCode::MovUp6 => Ok(Instruction::MovUp6),
            OpCode::MovUp7 => Ok(Instruction::MovUp7),
            OpCode::MovUp8 => Ok(Instruction::MovUp8),
            OpCode::MovUp9 => Ok(Instruction::MovUp9),
            OpCode::MovUp10 => Ok(Instruction::MovUp10),
            OpCode::MovUp11 => Ok(Instruction::MovUp11),
            OpCode::MovUp12 => Ok(Instruction::MovUp12),
            OpCode::MovUp13 => Ok(Instruction::MovUp13),
            OpCode::MovUp14 => Ok(Instruction::MovUp14),
            OpCode::MovUp15 => Ok(Instruction::MovUp15),
            OpCode::MovUpW2 => Ok(Instruction::MovUpW2),
            OpCode::MovUpW3 => Ok(Instruction::MovUpW3),
            OpCode::MovDn2 => Ok(Instruction::MovDn2),
            OpCode::MovDn3 => Ok(Instruction::MovDn3),
            OpCode::MovDn4 => Ok(Instruction::MovDn4),
            OpCode::MovDn5 => Ok(Instruction::MovDn5),
            OpCode::MovDn6 => Ok(Instruction::MovDn6),
            OpCode::MovDn7 => Ok(Instruction::MovDn7),
            OpCode::MovDn8 => Ok(Instruction::MovDn8),
            OpCode::MovDn9 => Ok(Instruction::MovDn9),
            OpCode::MovDn10 => Ok(Instruction::MovDn10),
            OpCode::MovDn11 => Ok(Instruction::MovDn11),
            OpCode::MovDn12 => Ok(Instruction::MovDn12),
            OpCode::MovDn13 => Ok(Instruction::MovDn13),
            OpCode::MovDn14 => Ok(Instruction::MovDn14),
            OpCode::MovDn15 => Ok(Instruction::MovDn15),
            OpCode::MovDnW2 => Ok(Instruction::MovDnW2),
            OpCode::MovDnW3 => Ok(Instruction::MovDnW3),
            OpCode::CSwap => Ok(Instruction::CSwap),
            OpCode::CSwapW => Ok(Instruction::CSwapW),
            OpCode::CDrop => Ok(Instruction::CDrop),
            OpCode::CDropW => Ok(Instruction::CDropW),

            // ----- input / output operations ----------------------------------------------------
            OpCode::PushU8 => Ok(Instruction::PushU8(source.read_u8()?)),
            OpCode::PushU16 => Ok(Instruction::PushU16(source.read_u16()?)),
            OpCode::PushU32 => Ok(Instruction::PushU32(source.read_u32()?)),
            OpCode::PushFelt => Ok(Instruction::PushFelt(Felt::read_from(source)?)),
            OpCode::PushWord => Ok(Instruction::PushWord([
                Felt::read_from(source)?,
                Felt::read_from(source)?,
                Felt::read_from(source)?,
                Felt::read_from(source)?,
            ])),
            OpCode::PushU8List => {
                let length = parse_num_push_params(source)?;
                (0..length)
                    .map(|_| source.read_u8())
                    .collect::<Result<_, _>>()
                    .map(Instruction::PushU8List)
            }
            OpCode::PushU16List => {
                let length = parse_num_push_params(source)?;
                (0..length)
                    .map(|_| source.read_u16())
                    .collect::<Result<_, _>>()
                    .map(Instruction::PushU16List)
            }
            OpCode::PushU32List => {
                let length = parse_num_push_params(source)?;
                (0..length)
                    .map(|_| source.read_u32())
                    .collect::<Result<_, _>>()
                    .map(Instruction::PushU32List)
            }
            OpCode::PushFeltList => {
                let length = parse_num_push_params(source)?;
                (0..length)
                    .map(|_| Felt::read_from(source))
                    .collect::<Result<_, _>>()
                    .map(Instruction::PushFeltList)
            }

            OpCode::Locaddr => Ok(Instruction::Locaddr(source.read_u16()?)),
            OpCode::Sdepth => Ok(Instruction::Sdepth),
            OpCode::Caller => Ok(Instruction::Caller),
            OpCode::Clk => Ok(Instruction::Clk),

            OpCode::MemLoad => Ok(Instruction::MemLoad),
            OpCode::MemLoadImm => Ok(Instruction::MemLoadImm(source.read_u32()?)),
            OpCode::MemLoadW => Ok(Instruction::MemLoadW),
            OpCode::MemLoadWImm => Ok(Instruction::MemLoadWImm(source.read_u32()?)),
            OpCode::LocLoad => Ok(Instruction::LocLoad(source.read_u16()?)),
            OpCode::LocLoadW => Ok(Instruction::LocLoadW(source.read_u16()?)),
            OpCode::MemStore => Ok(Instruction::MemStore),
            OpCode::MemStoreImm => Ok(Instruction::MemStoreImm(source.read_u32()?)),
            OpCode::LocStore => Ok(Instruction::LocStore(source.read_u16()?)),
            OpCode::MemStoreW => Ok(Instruction::MemStoreW),
            OpCode::MemStoreWImm => Ok(Instruction::MemStoreWImm(source.read_u32()?)),
            OpCode::LocStoreW => Ok(Instruction::LocStoreW(source.read_u16()?)),

            OpCode::MemStream => Ok(Instruction::MemStream),
            OpCode::AdvPipe => Ok(Instruction::AdvPipe),

            OpCode::AdvPush => Ok(Instruction::AdvPush(source.read_u8()?)),
            OpCode::AdvLoadW => Ok(Instruction::AdvLoadW),

            OpCode::AdvInject => Ok(Instruction::AdvInject(AdviceInjectorNode::read_from(source)?)),

            // ----- cryptographic operations -----------------------------------------------------
            OpCode::Hash => Ok(Instruction::Hash),
            OpCode::HMerge => Ok(Instruction::HMerge),
            OpCode::HPerm => Ok(Instruction::HPerm),
            OpCode::MTreeGet => Ok(Instruction::MTreeGet),
            OpCode::MTreeSet => Ok(Instruction::MTreeSet),
            OpCode::MTreeMerge => Ok(Instruction::MTreeMerge),
            OpCode::MTreeVerify => Ok(Instruction::MTreeVerify),

            // ----- STARK proof verification -----------------------------------------------------
            OpCode::FriExt2Fold4 => Ok(Instruction::FriExt2Fold4),
            OpCode::RCombBase => Ok(Instruction::RCombBase),

            // ----- exec / call ------------------------------------------------------------------
            OpCode::ExecLocal => Ok(Instruction::ExecLocal(source.read_u16()?)),
            OpCode::ExecImported => Ok(Instruction::ExecImported(ProcedureId::read_from(source)?)),
            OpCode::CallLocal => Ok(Instruction::CallLocal(source.read_u16()?)),
            OpCode::CallMastRoot => Ok(Instruction::CallMastRoot(RpoDigest::read_from(source)?)),
            OpCode::CallImported => Ok(Instruction::CallImported(ProcedureId::read_from(source)?)),
            OpCode::SysCall => Ok(Instruction::SysCall(ProcedureId::read_from(source)?)),
            OpCode::DynExec => Ok(Instruction::DynExec),
            OpCode::DynCall => Ok(Instruction::DynCall),
            OpCode::ProcRefLocal => Ok(Instruction::ProcRefLocal(source.read_u16()?)),
            OpCode::ProcRefImported => {
                Ok(Instruction::ProcRefImported(ProcedureId::read_from(source)?))
            }

            // ----- debugging --------------------------------------------------------------------
            OpCode::Debug => {
                let options = debug::read_options_from(source)?;
                Ok(Instruction::Debug(options))
            }

            // ----- event decorators -------------------------------------------------------------
            OpCode::Emit => Ok(Instruction::Emit(source.read_u32()?)),
            OpCode::Trace => Ok(Instruction::Trace(source.read_u32()?)),

            // ----- control flow -----------------------------------------------------------------
            // control flow instructions should be parsed as a part of Node::read_from() and we
            // should never get here
            OpCode::IfElse => unreachable!(),
            OpCode::Repeat => unreachable!(),
            OpCode::While => unreachable!(),
        }
    }
}

// HELPER FUNCTIONS
// ================================================================================================

fn parse_num_push_params<R: ByteReader>(source: &mut R) -> Result<u8, DeserializationError> {
    let length = source.read_u8()? as usize;
    if !(1..=MAX_PUSH_INPUTS).contains(&length) {
        Err(DeserializationError::InvalidValue("invalid push values argument".to_string()))
    } else {
        Ok(length as u8)
    }
}