Skip to main content

snarkvm_synthesizer_program/
bytes.rs

1// Copyright (c) 2019-2026 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 super::*;
17
18impl<N: Network> FromBytes for ProgramCore<N> {
19    fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
20        // Read the version.
21        let version = u8::read_le(&mut reader)?;
22        // Ensure the version is valid.
23        if version != 1 {
24            return Err(error("Invalid program version"));
25        }
26
27        // Read the program ID.
28        let id = ProgramID::read_le(&mut reader)?;
29
30        // Initialize the program.
31        let mut program = ProgramCore::new(id).map_err(error)?;
32
33        // Read the number of program imports.
34        let imports_len = u8::read_le(&mut reader)?;
35        // Read the program imports.
36        for _ in 0..imports_len {
37            program.add_import(Import::read_le(&mut reader)?).map_err(error)?;
38        }
39
40        // Read the number of components.
41        let components_len = u16::read_le(&mut reader)?;
42        for _ in 0..components_len {
43            // Read the variant.
44            let variant = u8::read_le(&mut reader)?;
45            // Match the variant.
46            match variant {
47                // Read the mapping.
48                0 => program.add_mapping(Mapping::read_le(&mut reader)?).map_err(error)?,
49                // Read the struct.
50                1 => program.add_struct(StructType::read_le(&mut reader)?).map_err(error)?,
51                // Read the record.
52                2 => program.add_record(RecordType::read_le(&mut reader)?).map_err(error)?,
53                // Read the closure.
54                3 => program.add_closure(ClosureCore::read_le(&mut reader)?).map_err(error)?,
55                // Read the function.
56                4 => program.add_function(FunctionCore::read_le(&mut reader)?).map_err(error)?,
57                // Read the constructor.
58                5 => program.add_constructor(ConstructorCore::read_le(&mut reader)?).map_err(error)?,
59                // Read the view function.
60                6 => program.add_view(ViewCore::read_le(&mut reader)?).map_err(error)?,
61                // Invalid variant.
62                _ => return Err(error(format!("Failed to parse program. Invalid component variant '{variant}'"))),
63            }
64        }
65
66        Ok(program)
67    }
68}
69
70impl<N: Network> ToBytes for ProgramCore<N> {
71    fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
72        // Write the version.
73        1u8.write_le(&mut writer)?;
74
75        // Write the program ID.
76        self.id.write_le(&mut writer)?;
77
78        // Write the number of program imports.
79        u8::try_from(self.imports.len()).map_err(error)?.write_le(&mut writer)?;
80        // Write the program imports.
81        for import in self.imports.values() {
82            import.write_le(&mut writer)?;
83        }
84
85        // Write the number of components.
86        u16::try_from(self.components.len()).map_err(error)?.write_le(&mut writer)?;
87
88        // Write the components.
89        for (label, definition) in self.components.iter() {
90            match label {
91                ProgramLabel::Constructor => {
92                    // Write the constructor, if it exists.
93                    if let Some(constructor) = &self.constructor {
94                        // Write the variant.
95                        5u8.write_le(&mut writer)?;
96                        // Write the constructor.
97                        constructor.write_le(&mut writer)?;
98                    }
99                }
100                ProgramLabel::Identifier(identifier) => {
101                    match definition {
102                        ProgramDefinition::Constructor => {
103                            return Err(error("A program constructor cannot have a named label"));
104                        }
105                        ProgramDefinition::Mapping => match self.mappings.get(identifier) {
106                            Some(mapping) => {
107                                // Write the variant.
108                                0u8.write_le(&mut writer)?;
109                                // Write the mapping.
110                                mapping.write_le(&mut writer)?;
111                            }
112                            None => return Err(error(format!("Mapping '{identifier}' is not defined"))),
113                        },
114                        ProgramDefinition::Struct => match self.structs.get(identifier) {
115                            Some(struct_) => {
116                                // Write the variant.
117                                1u8.write_le(&mut writer)?;
118                                // Write the struct.
119                                struct_.write_le(&mut writer)?;
120                            }
121                            None => return Err(error(format!("Struct '{identifier}' is not defined."))),
122                        },
123                        ProgramDefinition::Record => match self.records.get(identifier) {
124                            Some(record) => {
125                                // Write the variant.
126                                2u8.write_le(&mut writer)?;
127                                // Write the record.
128                                record.write_le(&mut writer)?;
129                            }
130                            None => return Err(error(format!("Record '{identifier}' is not defined."))),
131                        },
132                        ProgramDefinition::Closure => match self.closures.get(identifier) {
133                            Some(closure) => {
134                                // Write the variant.
135                                3u8.write_le(&mut writer)?;
136                                // Write the closure.
137                                closure.write_le(&mut writer)?;
138                            }
139                            None => return Err(error(format!("Closure '{identifier}' is not defined."))),
140                        },
141                        ProgramDefinition::Function => match self.functions.get(identifier) {
142                            Some(function) => {
143                                // Write the variant.
144                                4u8.write_le(&mut writer)?;
145                                // Write the function.
146                                function.write_le(&mut writer)?;
147                            }
148                            None => return Err(error(format!("Function '{identifier}' is not defined."))),
149                        },
150                        ProgramDefinition::View => match self.views.get(identifier) {
151                            Some(view) => {
152                                // Write the variant.
153                                6u8.write_le(&mut writer)?;
154                                // Write the view function.
155                                view.write_le(&mut writer)?;
156                            }
157                            None => return Err(error(format!("View '{identifier}' is not defined."))),
158                        },
159                    }
160                }
161            }
162        }
163        Ok(())
164    }
165}
166
167#[cfg(test)]
168mod tests {
169    use super::*;
170    use crate::Program;
171    use console::network::MainnetV0;
172
173    type CurrentNetwork = MainnetV0;
174
175    #[test]
176    fn test_bytes_with_view() -> Result<()> {
177        let program = r"
178program token_with_view.aleo;
179
180mapping balances:
181    key as address.public;
182    value as u64.public;
183
184function noop:
185    input r0 as u64.private;
186    output r0 as u64.private;
187
188view total_balance:
189    input r0 as address.public;
190    get.or_use balances[r0] 0u64 into r1;
191    output r1 as u64.public;";
192
193        let (string, expected) = Program::<CurrentNetwork>::parse(program).unwrap();
194        assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
195
196        let expected_bytes = expected.to_bytes_le()?;
197        let candidate = Program::<CurrentNetwork>::from_bytes_le(&expected_bytes)?;
198        assert_eq!(expected, candidate);
199        assert_eq!(expected_bytes, candidate.to_bytes_le()?);
200
201        Ok(())
202    }
203
204    #[test]
205    fn test_bytes() -> Result<()> {
206        let program = r"
207program token.aleo;
208
209record token:
210    owner as address.private;
211    token_amount as u64.private;
212
213function compute:
214    input r0 as token.record;
215    add r0.token_amount r0.token_amount into r1;
216    output r1 as u64.private;";
217
218        // Initialize a new program.
219        let (string, expected) = Program::<CurrentNetwork>::parse(program).unwrap();
220        assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
221
222        let expected_bytes = expected.to_bytes_le()?;
223
224        let candidate = Program::<CurrentNetwork>::from_bytes_le(&expected_bytes)?;
225        assert_eq!(expected, candidate);
226        assert_eq!(expected_bytes, candidate.to_bytes_le()?);
227
228        Ok(())
229    }
230}