snarkvm_synthesizer_program/logic/instruction/operation/
serialize.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use crate::{Opcode, Operand, RegistersCircuit, RegistersTrait, StackTrait};
17use console::{
18    network::prelude::*,
19    program::{ArrayType, Identifier, LiteralType, Plaintext, PlaintextType, Register, RegisterType, Value},
20};
21
22/// Serializes the bits of the input.
23pub type SerializeBits<N> = SerializeInstruction<N, { SerializeVariant::ToBits as u8 }>;
24/// Serializes the raw bits of the input.
25pub type SerializeBitsRaw<N> = SerializeInstruction<N, { SerializeVariant::ToBitsRaw as u8 }>;
26
27/// The serialize variant.
28#[derive(Debug, Clone, Eq, PartialEq)]
29pub enum SerializeVariant {
30    ToBits,
31    ToBitsRaw,
32}
33
34impl SerializeVariant {
35    // Returns the opcode associated with the variant.
36    pub const fn opcode(variant: u8) -> &'static str {
37        match variant {
38            0 => "serialize.bits",
39            1 => "serialize.bits.raw",
40            _ => panic!("Invalid 'serialize' instruction opcode"),
41        }
42    }
43}
44
45/// Checks that the number of operands is correct.
46fn check_number_of_operands(variant: u8, num_operands: usize) -> Result<()> {
47    if num_operands != 1 {
48        bail!("Instruction '{}' expects 1 operand, found {num_operands} operands", SerializeVariant::opcode(variant))
49    }
50    Ok(())
51}
52
53/// Checks that the operand type is valid.
54fn check_operand_type_is_valid(variant: u8, operand_type: &PlaintextType<impl Network>) -> Result<()> {
55    // A helper function to check a literal type.
56    fn check_literal_type(literal_type: &LiteralType) -> Result<()> {
57        match literal_type {
58            LiteralType::Address
59            | LiteralType::Boolean
60            | LiteralType::Field
61            | LiteralType::Group
62            | LiteralType::I8
63            | LiteralType::I16
64            | LiteralType::I32
65            | LiteralType::I64
66            | LiteralType::I128
67            | LiteralType::U8
68            | LiteralType::U16
69            | LiteralType::U32
70            | LiteralType::U64
71            | LiteralType::U128
72            | LiteralType::Scalar => Ok(()),
73            _ => bail!("Invalid literal type '{literal_type}' for 'serialize' instruction"),
74        }
75    }
76
77    match operand_type {
78        PlaintextType::Literal(literal_type) => check_literal_type(literal_type),
79        PlaintextType::Array(array_type) => match array_type.base_element_type() {
80            PlaintextType::Literal(literal_type) => check_literal_type(literal_type),
81            _ => bail!("Invalid element type '{array_type}' for 'serialize' instruction"),
82        },
83        _ => bail!("Instruction '{}' cannot take type '{operand_type}' as input", SerializeVariant::opcode(variant)),
84    }
85}
86
87/// Check that the destination type is valid.
88fn check_destination_type_is_valid(variant: u8, destination_type: &ArrayType<impl Network>) -> Result<()> {
89    match (variant, destination_type) {
90        (0 | 1, array_type) if array_type.is_bit_array() => Ok(()),
91        _ => {
92            bail!("Instruction '{}' cannot output type '{destination_type}'", SerializeVariant::opcode(variant))
93        }
94    }
95}
96
97/// Serialize the operand into the declared type.
98#[derive(Clone, PartialEq, Eq, Hash)]
99pub struct SerializeInstruction<N: Network, const VARIANT: u8> {
100    /// The operand as `input`.
101    operands: Vec<Operand<N>>,
102    /// The operand type.
103    operand_type: PlaintextType<N>,
104    /// The destination register.
105    destination: Register<N>,
106    /// The destination register type.
107    destination_type: ArrayType<N>,
108}
109
110impl<N: Network, const VARIANT: u8> SerializeInstruction<N, VARIANT> {
111    /// Initializes a new `serialize` instruction.
112    pub fn new(
113        operands: Vec<Operand<N>>,
114        operand_type: PlaintextType<N>,
115        destination: Register<N>,
116        destination_type: ArrayType<N>,
117    ) -> Result<Self> {
118        // Sanity check the number of operands.
119        check_number_of_operands(VARIANT, operands.len())?;
120        // Ensure that the operand type is valid.
121        check_operand_type_is_valid(VARIANT, &operand_type)?;
122        // Sanity check the destination type.
123        check_destination_type_is_valid(VARIANT, &destination_type)?;
124        // Return the instruction.
125        Ok(Self { operands, operand_type, destination, destination_type })
126    }
127
128    /// Returns the opcode.
129    pub const fn opcode() -> Opcode {
130        Opcode::Serialize(SerializeVariant::opcode(VARIANT))
131    }
132
133    /// Returns the operands in the operation.
134    pub fn operands(&self) -> &[Operand<N>] {
135        // Sanity check that the operands is the correct length.
136        if cfg!(debug_assertions) {
137            check_number_of_operands(VARIANT, self.operands.len()).unwrap();
138            check_operand_type_is_valid(VARIANT, &self.operand_type).unwrap();
139            check_destination_type_is_valid(VARIANT, &self.destination_type).unwrap();
140        }
141        // Return the operand.
142        &self.operands
143    }
144
145    /// Returns the operand type.
146    pub const fn operand_type(&self) -> &PlaintextType<N> {
147        &self.operand_type
148    }
149
150    /// Returns the destination register.
151    #[inline]
152    pub fn destinations(&self) -> Vec<Register<N>> {
153        vec![self.destination.clone()]
154    }
155
156    /// Returns the destination register type.
157    #[inline]
158    pub const fn destination_type(&self) -> &ArrayType<N> {
159        &self.destination_type
160    }
161}
162
163/// Evaluate a `serialize` operation.
164///
165/// This allows running `serialize` without the machinery of stacks and registers.
166/// This is necessary for the Leo interpreter.
167pub fn evaluate_serialize<N: Network>(
168    variant: SerializeVariant,
169    input: &Value<N>,
170    destination_type: &ArrayType<N>,
171) -> Result<Value<N>> {
172    evaluate_serialize_internal(variant as u8, input, destination_type)
173}
174
175fn evaluate_serialize_internal<N: Network>(
176    variant: u8,
177    input: &Value<N>,
178    destination_type: &ArrayType<N>,
179) -> Result<Value<N>> {
180    match (variant, destination_type) {
181        (0, array_type) if array_type.is_bit_array() => {
182            // Get the desired length of the array.
183            let length = **array_type.length();
184            // Serialize the input to bits.
185            let bits = input.to_bits_le();
186            // Return the bits as a plaintext array.
187            Ok(Value::Plaintext(Plaintext::from_bit_array(bits, length)?))
188        }
189        (1, array_type) if array_type.is_bit_array() => {
190            // Get the desired length of the array.
191            let length = **array_type.length();
192            // Serialize the input to raw bits.
193            let bits = input.to_bits_raw_le();
194            // Return the bits as a plaintext array.
195            Ok(Value::Plaintext(Plaintext::from_bit_array(bits, length)?))
196        }
197        _ => bail!(
198            "Invalid destination type '{}' for instruction '{}'",
199            destination_type,
200            SerializeVariant::opcode(variant)
201        ),
202    }
203}
204
205impl<N: Network, const VARIANT: u8> SerializeInstruction<N, VARIANT> {
206    /// Evaluates the instruction.
207    pub fn evaluate(&self, stack: &impl StackTrait<N>, registers: &mut impl RegistersTrait<N>) -> Result<()> {
208        // Ensure the number of operands is correct.
209        check_number_of_operands(VARIANT, self.operands.len())?;
210        // Ensure that the operand type is valid.
211        check_operand_type_is_valid(VARIANT, &self.operand_type)?;
212        // Ensure the destination type is valid.
213        check_destination_type_is_valid(VARIANT, &self.destination_type)?;
214
215        // Load the operand.
216        let input = registers.load(stack, &self.operands[0])?;
217
218        let output = evaluate_serialize_internal(VARIANT, &input, &self.destination_type)?;
219
220        // Store the output.
221        registers.store(stack, &self.destination, output)
222    }
223
224    /// Executes the instruction.
225    pub fn execute<A: circuit::Aleo<Network = N>>(
226        &self,
227        stack: &impl StackTrait<N>,
228        registers: &mut impl RegistersCircuit<N, A>,
229    ) -> Result<()> {
230        use crate::circuit::traits::{ToBits, ToBitsRaw};
231
232        // Ensure the number of operands is correct.
233        check_number_of_operands(VARIANT, self.operands.len())?;
234        // Ensure that the operand type is valid.
235        check_operand_type_is_valid(VARIANT, &self.operand_type)?;
236        // Ensure the destination type is valid.
237        check_destination_type_is_valid(VARIANT, &self.destination_type)?;
238
239        // Load the operand.
240        let input = registers.load_circuit(stack, &self.operands[0])?;
241
242        let output = match (VARIANT, &self.destination_type) {
243            (0, array_type) if array_type.is_bit_array() => {
244                // Get the desired length of the array.
245                let length = **array_type.length();
246                // Serialize the input to bits.
247                let bits = input.to_bits_le();
248                // Return the bits as a plaintext array.
249                circuit::Value::Plaintext(circuit::Plaintext::from_bit_array(bits, length)?)
250            }
251            (1, array_type) if array_type.is_bit_array() => {
252                // Get the desired length of the array.
253                let length = **array_type.length();
254                // Serialize the input to raw bits.
255                let bits = input.to_bits_raw_le();
256                // Return the bits as a plaintext array.
257                circuit::Value::Plaintext(circuit::Plaintext::from_bit_array(bits, length)?)
258            }
259            _ => bail!("Invalid destination type '{}' for instruction '{}'", &self.destination_type, Self::opcode(),),
260        };
261
262        // Store the output.
263        registers.store_circuit(stack, &self.destination, output)
264    }
265
266    /// Finalizes the instruction.
267    #[inline]
268    pub fn finalize(&self, stack: &impl StackTrait<N>, registers: &mut impl RegistersTrait<N>) -> Result<()> {
269        self.evaluate(stack, registers)
270    }
271
272    /// Returns the output type from the given program and input types.
273    pub fn output_types(
274        &self,
275        stack: &impl StackTrait<N>,
276        input_types: &[RegisterType<N>],
277    ) -> Result<Vec<RegisterType<N>>> {
278        // Ensure the number of operands is correct.
279        check_number_of_operands(VARIANT, self.operands.len())?;
280        // Ensure the operand type is valid.
281        check_operand_type_is_valid(VARIANT, &self.operand_type)?;
282        // Ensure the destination type is valid.
283        check_destination_type_is_valid(VARIANT, &self.destination_type)?;
284
285        // Check that the input type matches the operand type.
286        ensure!(input_types.len() == 1, "Expected exactly one input type");
287        match &input_types[0] {
288            RegisterType::Plaintext(plaintext_type) => {
289                ensure!(
290                    plaintext_type == &self.operand_type,
291                    "Input type {} does not match operand type {}",
292                    input_types[0],
293                    self.operand_type
294                )
295            }
296            type_ => bail!("Input type {type_} does not match operand type {}", self.operand_type),
297        }
298
299        // A helper to get a struct declaration.
300        let get_struct = |identifier: &Identifier<N>| stack.program().get_struct(identifier).cloned();
301
302        // Get the size in bits of the operand.
303        let size_in_bits = match VARIANT {
304            0 => self.operand_type.size_in_bits(&get_struct)?,
305            1 => self.operand_type.size_in_bits_raw(&get_struct)?,
306            variant => bail!("Invalid `serialize` variant '{variant}'"),
307        };
308
309        // Check that the number of bits of the operand matches the destination.
310        ensure!(
311            size_in_bits == **self.destination_type.length() as usize,
312            "The number of bits of the operand '{size_in_bits}' does not match the destination '{}'",
313            **self.destination_type.length()
314        );
315
316        Ok(vec![RegisterType::Plaintext(PlaintextType::Array(self.destination_type.clone()))])
317    }
318}
319
320impl<N: Network, const VARIANT: u8> Parser for SerializeInstruction<N, VARIANT> {
321    /// Parses a string into an operation.
322    fn parse(string: &str) -> ParserResult<Self> {
323        /// Parse the operands from the string.
324        fn parse_operands<N: Network>(string: &str, num_operands: usize) -> ParserResult<Vec<Operand<N>>> {
325            let mut operands = Vec::with_capacity(num_operands);
326            let mut string = string;
327
328            for _ in 0..num_operands {
329                // Parse the whitespace from the string.
330                let (next_string, _) = Sanitizer::parse_whitespaces(string)?;
331                // Parse the operand from the string.
332                let (next_string, operand) = Operand::parse(next_string)?;
333                // Update the string.
334                string = next_string;
335                // Push the operand.
336                operands.push(operand);
337            }
338
339            Ok((string, operands))
340        }
341
342        // Parse the opcode from the string.
343        let (string, _) = tag(*Self::opcode())(string)?;
344        // Parse the operands from the string.
345        let (string, operands) = parse_operands(string, 1)?;
346
347        // Parse the whitespace from the string.
348        let (string, _) = Sanitizer::parse_whitespaces(string)?;
349        // Parse the "(" from the string.
350        let (string, _) = tag("(")(string)?;
351        // Parse the whitespace from the string.
352        let (string, _) = Sanitizer::parse_whitespaces(string)?;
353        // Parse the operand type from the string.
354        let (string, operand_type) = PlaintextType::parse(string)?;
355        // Parse the ")" from the string.
356        let (string, _) = tag(")")(string)?;
357
358        // Parse the whitespace from the string.
359        let (string, _) = Sanitizer::parse_whitespaces(string)?;
360        // Parse the "into" from the string.
361        let (string, _) = tag("into")(string)?;
362        // Parse the whitespace from the string.
363        let (string, _) = Sanitizer::parse_whitespaces(string)?;
364        // Parse the destination register from the string.
365        let (string, destination) = Register::parse(string)?;
366
367        // Parse the whitespace from the string.
368        let (string, _) = Sanitizer::parse_whitespaces(string)?;
369        // Parse the "(" from the string.
370        let (string, _) = tag("(")(string)?;
371        // Parse the whitespace from the string.
372        let (string, _) = Sanitizer::parse_whitespaces(string)?;
373        // Parse the destination register type from the string.
374        let (string, destination_type) = ArrayType::parse(string)?;
375        // Parse the ")" from the string.
376        let (string, _) = tag(")")(string)?;
377
378        // Construct the instruction, checking for errors.
379        match Self::new(operands, operand_type, destination, destination_type) {
380            Ok(instruction) => Ok((string, instruction)),
381            Err(e) => map_res(fail, |_: ParserResult<Self>| {
382                Err(error(format!("Failed to parse '{}' instruction: {e}", Self::opcode())))
383            })(string),
384        }
385    }
386}
387
388impl<N: Network, const VARIANT: u8> FromStr for SerializeInstruction<N, VARIANT> {
389    type Err = Error;
390
391    /// Parses a string into an operation.
392    fn from_str(string: &str) -> Result<Self> {
393        match Self::parse(string) {
394            Ok((remainder, object)) => {
395                // Ensure the remainder is empty.
396                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
397                // Return the object.
398                Ok(object)
399            }
400            Err(error) => bail!("Failed to parse string. {error}"),
401        }
402    }
403}
404
405impl<N: Network, const VARIANT: u8> Debug for SerializeInstruction<N, VARIANT> {
406    /// Prints the operation as a string.
407    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
408        Display::fmt(self, f)
409    }
410}
411
412impl<N: Network, const VARIANT: u8> Display for SerializeInstruction<N, VARIANT> {
413    /// Prints the operation to a string.
414    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
415        write!(f, "{} ", Self::opcode())?;
416        self.operands.iter().try_for_each(|operand| write!(f, "{operand} "))?;
417        write!(f, " ({}) into {} ({})", self.operand_type, self.destination, self.destination_type)
418    }
419}
420
421impl<N: Network, const VARIANT: u8> FromBytes for SerializeInstruction<N, VARIANT> {
422    /// Reads the operation from a buffer.
423    fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
424        // Read the operand.
425        let operand = Operand::read_le(&mut reader)?;
426        // Read the operand type.
427        let operand_type = PlaintextType::read_le(&mut reader)?;
428        // Read the destination register.
429        let destination = Register::read_le(&mut reader)?;
430        // Read the destination register type.
431        let destination_type = ArrayType::read_le(&mut reader)?;
432        // Return the operation.
433        match Self::new(vec![operand], operand_type, destination, destination_type) {
434            Ok(instruction) => Ok(instruction),
435            Err(e) => Err(error(format!("Failed to read '{}' instruction: {e}", Self::opcode()))),
436        }
437    }
438}
439
440impl<N: Network, const VARIANT: u8> ToBytes for SerializeInstruction<N, VARIANT> {
441    /// Writes the operation to a buffer.
442    fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
443        // Write the operands.
444        self.operands.iter().try_for_each(|operand| operand.write_le(&mut writer))?;
445        // Write the operand type.
446        self.operand_type.write_le(&mut writer)?;
447        // Write the destination register.
448        self.destination.write_le(&mut writer)?;
449        // Write the destination register type.
450        self.destination_type.write_le(&mut writer)
451    }
452}
453
454#[cfg(test)]
455mod tests {
456    use super::*;
457    use console::{network::MainnetV0, types::U32};
458
459    type CurrentNetwork = MainnetV0;
460
461    /// **Attention**: When changing this, also update in `tests/instruction/serialize.rs`.
462    fn valid_source_types<N: Network>() -> &'static [PlaintextType<N>] {
463        &[
464            PlaintextType::Literal(LiteralType::Address),
465            PlaintextType::Literal(LiteralType::Field),
466            PlaintextType::Literal(LiteralType::Group),
467            PlaintextType::Literal(LiteralType::I8),
468            PlaintextType::Literal(LiteralType::I16),
469            PlaintextType::Literal(LiteralType::I32),
470            PlaintextType::Literal(LiteralType::I128),
471            PlaintextType::Literal(LiteralType::I64),
472            PlaintextType::Literal(LiteralType::U8),
473            PlaintextType::Literal(LiteralType::U16),
474            PlaintextType::Literal(LiteralType::U32),
475            PlaintextType::Literal(LiteralType::U64),
476            PlaintextType::Literal(LiteralType::U128),
477            PlaintextType::Literal(LiteralType::Scalar),
478        ]
479    }
480
481    /// Randomly sample a destination type.
482    fn sample_destination_type<N: Network, const VARIANT: u8>(rng: &mut TestRng) -> ArrayType<N> {
483        // Generate a random array length between 1 and N::MAX_ARRAY_ELEMENTS.
484        let array_length = 1 + (u32::rand(rng) % u32::try_from(N::MAX_ARRAY_ELEMENTS).unwrap());
485        match VARIANT {
486            0 | 1 => {
487                ArrayType::new(PlaintextType::Literal(LiteralType::Boolean), vec![U32::new(array_length)]).unwrap()
488            }
489            _ => panic!("Invalid variant"),
490        }
491    }
492
493    fn run_parser_test<const VARIANT: u8>(rng: &mut TestRng) {
494        for source_type in valid_source_types() {
495            {
496                let opcode = SerializeVariant::opcode(VARIANT);
497                let destination_type = sample_destination_type::<CurrentNetwork, VARIANT>(rng);
498                let instruction = format!("{opcode} r0 ({source_type}) into r1 ({destination_type})");
499                println!("Parsing instruction: '{instruction}'");
500
501                let (string, serialize) = SerializeInstruction::<CurrentNetwork, VARIANT>::parse(&instruction).unwrap();
502                assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
503                assert_eq!(serialize.operands.len(), 1, "The number of operands is incorrect");
504                assert_eq!(
505                    serialize.operands[0],
506                    Operand::Register(Register::Locator(0)),
507                    "The first operand is incorrect"
508                );
509                assert_eq!(&serialize.operand_type, source_type, "The operand type is incorrect");
510                assert_eq!(serialize.destination, Register::Locator(1), "The destination register is incorrect");
511                assert_eq!(&serialize.destination_type, &destination_type, "The destination type is incorrect");
512            }
513        }
514    }
515
516    #[test]
517    fn test_parse() {
518        // Initialize an RNG.
519        let rng = &mut TestRng::default();
520
521        // Run the parser test for each variant.
522        run_parser_test::<{ SerializeVariant::ToBits as u8 }>(rng);
523        run_parser_test::<{ SerializeVariant::ToBitsRaw as u8 }>(rng);
524
525        SerializeBitsRaw::<CurrentNetwork>::from_str("serialize.bits.raw r0 (boolean) into r1 ([boolean; 1u32])")
526            .unwrap();
527    }
528}