snarkvm_synthesizer_program/
lib.rs

1// Copyright 2024-2025 Aleo Network Foundation
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
16#![forbid(unsafe_code)]
17#![allow(clippy::too_many_arguments)]
18#![warn(clippy::cast_possible_truncation)]
19
20pub type Program<N> = crate::ProgramCore<N, Instruction<N>, Command<N>>;
21pub type Function<N> = crate::FunctionCore<N, Instruction<N>, Command<N>>;
22pub type Finalize<N> = crate::FinalizeCore<N, Command<N>>;
23pub type Closure<N> = crate::ClosureCore<N, Instruction<N>>;
24
25mod closure;
26pub use closure::*;
27
28pub mod finalize;
29pub use finalize::*;
30
31mod function;
32pub use function::*;
33
34mod import;
35pub use import::*;
36
37pub mod logic;
38pub use logic::*;
39
40mod mapping;
41pub use mapping::*;
42
43pub mod traits;
44pub use traits::*;
45
46mod bytes;
47mod parse;
48mod serialize;
49
50use console::{
51    network::prelude::{
52        Debug,
53        Deserialize,
54        Deserializer,
55        Display,
56        Err,
57        Error,
58        ErrorKind,
59        Formatter,
60        FromBytes,
61        FromBytesDeserializer,
62        FromStr,
63        IoResult,
64        Network,
65        Parser,
66        ParserResult,
67        Read,
68        Result,
69        Sanitizer,
70        Serialize,
71        Serializer,
72        ToBytes,
73        ToBytesSerializer,
74        TypeName,
75        Write,
76        anyhow,
77        bail,
78        de,
79        ensure,
80        error,
81        fmt,
82        make_error,
83        many0,
84        many1,
85        map,
86        map_res,
87        tag,
88        take,
89    },
90    program::{Identifier, PlaintextType, ProgramID, RecordType, StructType},
91};
92
93use indexmap::IndexMap;
94
95#[derive(Copy, Clone, PartialEq, Eq, Hash)]
96enum ProgramDefinition {
97    /// A program mapping.
98    Mapping,
99    /// A program struct.
100    Struct,
101    /// A program record.
102    Record,
103    /// A program closure.
104    Closure,
105    /// A program function.
106    Function,
107}
108
109#[derive(Clone, PartialEq, Eq)]
110pub struct ProgramCore<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> {
111    /// The ID of the program.
112    id: ProgramID<N>,
113    /// A map of the declared imports for the program.
114    imports: IndexMap<ProgramID<N>, Import<N>>,
115    /// A map of identifiers to their program declaration.
116    identifiers: IndexMap<Identifier<N>, ProgramDefinition>,
117    /// A map of the declared mappings for the program.
118    mappings: IndexMap<Identifier<N>, Mapping<N>>,
119    /// A map of the declared structs for the program.
120    structs: IndexMap<Identifier<N>, StructType<N>>,
121    /// A map of the declared record types for the program.
122    records: IndexMap<Identifier<N>, RecordType<N>>,
123    /// A map of the declared closures for the program.
124    closures: IndexMap<Identifier<N>, ClosureCore<N, Instruction>>,
125    /// A map of the declared functions for the program.
126    functions: IndexMap<Identifier<N>, FunctionCore<N, Instruction, Command>>,
127}
128
129impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> ProgramCore<N, Instruction, Command> {
130    /// Initializes an empty program.
131    #[inline]
132    pub fn new(id: ProgramID<N>) -> Result<Self> {
133        // Ensure the program name is valid.
134        ensure!(!Self::is_reserved_keyword(id.name()), "Program name is invalid: {}", id.name());
135
136        Ok(Self {
137            id,
138            imports: IndexMap::new(),
139            identifiers: IndexMap::new(),
140            mappings: IndexMap::new(),
141            structs: IndexMap::new(),
142            records: IndexMap::new(),
143            closures: IndexMap::new(),
144            functions: IndexMap::new(),
145        })
146    }
147
148    /// Initializes the credits program.
149    #[inline]
150    pub fn credits() -> Result<Self> {
151        Self::from_str(include_str!("./resources/credits.aleo"))
152    }
153
154    /// Returns the ID of the program.
155    pub const fn id(&self) -> &ProgramID<N> {
156        &self.id
157    }
158
159    /// Returns the imports in the program.
160    pub const fn imports(&self) -> &IndexMap<ProgramID<N>, Import<N>> {
161        &self.imports
162    }
163
164    /// Returns the mappings in the program.
165    pub const fn mappings(&self) -> &IndexMap<Identifier<N>, Mapping<N>> {
166        &self.mappings
167    }
168
169    /// Returns the structs in the program.
170    pub const fn structs(&self) -> &IndexMap<Identifier<N>, StructType<N>> {
171        &self.structs
172    }
173
174    /// Returns the records in the program.
175    pub const fn records(&self) -> &IndexMap<Identifier<N>, RecordType<N>> {
176        &self.records
177    }
178
179    /// Returns the closures in the program.
180    pub const fn closures(&self) -> &IndexMap<Identifier<N>, ClosureCore<N, Instruction>> {
181        &self.closures
182    }
183
184    /// Returns the functions in the program.
185    pub const fn functions(&self) -> &IndexMap<Identifier<N>, FunctionCore<N, Instruction, Command>> {
186        &self.functions
187    }
188
189    /// Returns `true` if the program contains an import with the given program ID.
190    pub fn contains_import(&self, id: &ProgramID<N>) -> bool {
191        self.imports.contains_key(id)
192    }
193
194    /// Returns `true` if the program contains a mapping with the given name.
195    pub fn contains_mapping(&self, name: &Identifier<N>) -> bool {
196        self.mappings.contains_key(name)
197    }
198
199    /// Returns `true` if the program contains a struct with the given name.
200    pub fn contains_struct(&self, name: &Identifier<N>) -> bool {
201        self.structs.contains_key(name)
202    }
203
204    /// Returns `true` if the program contains a record with the given name.
205    pub fn contains_record(&self, name: &Identifier<N>) -> bool {
206        self.records.contains_key(name)
207    }
208
209    /// Returns `true` if the program contains a closure with the given name.
210    pub fn contains_closure(&self, name: &Identifier<N>) -> bool {
211        self.closures.contains_key(name)
212    }
213
214    /// Returns `true` if the program contains a function with the given name.
215    pub fn contains_function(&self, name: &Identifier<N>) -> bool {
216        self.functions.contains_key(name)
217    }
218
219    /// Returns the mapping with the given name.
220    pub fn get_mapping(&self, name: &Identifier<N>) -> Result<Mapping<N>> {
221        // Attempt to retrieve the mapping.
222        let mapping = self.mappings.get(name).cloned().ok_or_else(|| anyhow!("Mapping '{name}' is not defined."))?;
223        // Ensure the mapping name matches.
224        ensure!(mapping.name() == name, "Expected mapping '{name}', but found mapping '{}'", mapping.name());
225        // Return the mapping.
226        Ok(mapping)
227    }
228
229    /// Returns the struct with the given name.
230    pub fn get_struct(&self, name: &Identifier<N>) -> Result<&StructType<N>> {
231        // Attempt to retrieve the struct.
232        let struct_ = self.structs.get(name).ok_or_else(|| anyhow!("Struct '{name}' is not defined."))?;
233        // Ensure the struct name matches.
234        ensure!(struct_.name() == name, "Expected struct '{name}', but found struct '{}'", struct_.name());
235        // Ensure the struct contains members.
236        ensure!(!struct_.members().is_empty(), "Struct '{name}' is missing members.");
237        // Return the struct.
238        Ok(struct_)
239    }
240
241    /// Returns the record with the given name.
242    pub fn get_record(&self, name: &Identifier<N>) -> Result<&RecordType<N>> {
243        // Attempt to retrieve the record.
244        let record = self.records.get(name).ok_or_else(|| anyhow!("Record '{name}' is not defined."))?;
245        // Ensure the record name matches.
246        ensure!(record.name() == name, "Expected record '{name}', but found record '{}'", record.name());
247        // Return the record.
248        Ok(record)
249    }
250
251    /// Returns the closure with the given name.
252    pub fn get_closure(&self, name: &Identifier<N>) -> Result<ClosureCore<N, Instruction>> {
253        // Attempt to retrieve the closure.
254        let closure = self.closures.get(name).cloned().ok_or_else(|| anyhow!("Closure '{name}' is not defined."))?;
255        // Ensure the closure name matches.
256        ensure!(closure.name() == name, "Expected closure '{name}', but found closure '{}'", closure.name());
257        // Ensure there are input statements in the closure.
258        ensure!(!closure.inputs().is_empty(), "Cannot evaluate a closure without input statements");
259        // Ensure the number of inputs is within the allowed range.
260        ensure!(closure.inputs().len() <= N::MAX_INPUTS, "Closure exceeds maximum number of inputs");
261        // Ensure there are instructions in the closure.
262        ensure!(!closure.instructions().is_empty(), "Cannot evaluate a closure without instructions");
263        // Ensure the number of outputs is within the allowed range.
264        ensure!(closure.outputs().len() <= N::MAX_OUTPUTS, "Closure exceeds maximum number of outputs");
265        // Return the closure.
266        Ok(closure)
267    }
268
269    /// Returns the function with the given name.
270    pub fn get_function(&self, name: &Identifier<N>) -> Result<FunctionCore<N, Instruction, Command>> {
271        self.get_function_ref(name).cloned()
272    }
273
274    /// Returns a reference to the function with the given name.
275    pub fn get_function_ref(&self, name: &Identifier<N>) -> Result<&FunctionCore<N, Instruction, Command>> {
276        // Attempt to retrieve the function.
277        let function = self.functions.get(name).ok_or(anyhow!("Function '{}/{name}' is not defined.", self.id))?;
278        // Ensure the function name matches.
279        ensure!(function.name() == name, "Expected function '{name}', but found function '{}'", function.name());
280        // Ensure the number of inputs is within the allowed range.
281        ensure!(function.inputs().len() <= N::MAX_INPUTS, "Function exceeds maximum number of inputs");
282        // Ensure the number of instructions is within the allowed range.
283        ensure!(function.instructions().len() <= N::MAX_INSTRUCTIONS, "Function exceeds maximum instructions");
284        // Ensure the number of outputs is within the allowed range.
285        ensure!(function.outputs().len() <= N::MAX_OUTPUTS, "Function exceeds maximum number of outputs");
286        // Return the function.
287        Ok(function)
288    }
289}
290
291impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> ProgramCore<N, Instruction, Command> {
292    /// Adds a new import statement to the program.
293    ///
294    /// # Errors
295    /// This method will halt if the imported program was previously added.
296    #[inline]
297    fn add_import(&mut self, import: Import<N>) -> Result<()> {
298        // Retrieve the imported program name.
299        let import_name = *import.name();
300
301        // Ensure that the number of imports is within the allowed range.
302        ensure!(self.imports.len() < N::MAX_IMPORTS, "Program exceeds the maximum number of imports");
303
304        // Ensure the import name is new.
305        ensure!(self.is_unique_name(&import_name), "'{import_name}' is already in use.");
306        // Ensure the import name is not a reserved opcode.
307        ensure!(!Self::is_reserved_opcode(&import_name.to_string()), "'{import_name}' is a reserved opcode.");
308        // Ensure the import name is not a reserved keyword.
309        ensure!(!Self::is_reserved_keyword(&import_name), "'{import_name}' is a reserved keyword.");
310
311        // Ensure the import is new.
312        ensure!(
313            !self.imports.contains_key(import.program_id()),
314            "Import '{}' is already defined.",
315            import.program_id()
316        );
317
318        // Add the import statement to the program.
319        if self.imports.insert(*import.program_id(), import.clone()).is_some() {
320            bail!("'{}' already exists in the program.", import.program_id())
321        }
322        Ok(())
323    }
324
325    /// Adds a new mapping to the program.
326    ///
327    /// # Errors
328    /// This method will halt if the mapping name is already in use.
329    /// This method will halt if the mapping name is a reserved opcode or keyword.
330    #[inline]
331    fn add_mapping(&mut self, mapping: Mapping<N>) -> Result<()> {
332        // Retrieve the mapping name.
333        let mapping_name = *mapping.name();
334
335        // Ensure the program has not exceeded the maximum number of mappings.
336        ensure!(self.mappings.len() < N::MAX_MAPPINGS, "Program exceeds the maximum number of mappings");
337
338        // Ensure the mapping name is new.
339        ensure!(self.is_unique_name(&mapping_name), "'{mapping_name}' is already in use.");
340        // Ensure the mapping name is not a reserved keyword.
341        ensure!(!Self::is_reserved_keyword(&mapping_name), "'{mapping_name}' is a reserved keyword.");
342        // Ensure the mapping name is not a reserved opcode.
343        ensure!(!Self::is_reserved_opcode(&mapping_name.to_string()), "'{mapping_name}' is a reserved opcode.");
344
345        // Add the mapping name to the identifiers.
346        if self.identifiers.insert(mapping_name, ProgramDefinition::Mapping).is_some() {
347            bail!("'{mapping_name}' already exists in the program.")
348        }
349        // Add the mapping to the program.
350        if self.mappings.insert(mapping_name, mapping).is_some() {
351            bail!("'{mapping_name}' already exists in the program.")
352        }
353        Ok(())
354    }
355
356    /// Adds a new struct to the program.
357    ///
358    /// # Errors
359    /// This method will halt if the struct was previously added.
360    /// This method will halt if the struct name is already in use in the program.
361    /// This method will halt if the struct name is a reserved opcode or keyword.
362    /// This method will halt if any structs in the struct's members are not already defined.
363    #[inline]
364    fn add_struct(&mut self, struct_: StructType<N>) -> Result<()> {
365        // Retrieve the struct name.
366        let struct_name = *struct_.name();
367
368        // Ensure the program has not exceeded the maximum number of structs.
369        ensure!(self.structs.len() < N::MAX_STRUCTS, "Program exceeds the maximum number of structs.");
370
371        // Ensure the struct name is new.
372        ensure!(self.is_unique_name(&struct_name), "'{struct_name}' is already in use.");
373        // Ensure the struct name is not a reserved opcode.
374        ensure!(!Self::is_reserved_opcode(&struct_name.to_string()), "'{struct_name}' is a reserved opcode.");
375        // Ensure the struct name is not a reserved keyword.
376        ensure!(!Self::is_reserved_keyword(&struct_name), "'{struct_name}' is a reserved keyword.");
377
378        // Ensure the struct contains members.
379        ensure!(!struct_.members().is_empty(), "Struct '{struct_name}' is missing members.");
380
381        // Ensure all struct members are well-formed.
382        // Note: This design ensures cyclic references are not possible.
383        for (identifier, plaintext_type) in struct_.members() {
384            // Ensure the member name is not a reserved keyword.
385            ensure!(!Self::is_reserved_keyword(identifier), "'{identifier}' is a reserved keyword.");
386            // Ensure the member type is already defined in the program.
387            match plaintext_type {
388                PlaintextType::Literal(_) => continue,
389                PlaintextType::Struct(member_identifier) => {
390                    // Ensure the member struct name exists in the program.
391                    if !self.structs.contains_key(member_identifier) {
392                        bail!("'{member_identifier}' in struct '{}' is not defined.", struct_name)
393                    }
394                }
395                PlaintextType::Array(array_type) => {
396                    if let PlaintextType::Struct(struct_name) = array_type.base_element_type() {
397                        // Ensure the member struct name exists in the program.
398                        if !self.structs.contains_key(struct_name) {
399                            bail!("'{struct_name}' in array '{array_type}' is not defined.")
400                        }
401                    }
402                }
403            }
404        }
405
406        // Add the struct name to the identifiers.
407        if self.identifiers.insert(struct_name, ProgramDefinition::Struct).is_some() {
408            bail!("'{}' already exists in the program.", struct_name)
409        }
410        // Add the struct to the program.
411        if self.structs.insert(struct_name, struct_).is_some() {
412            bail!("'{}' already exists in the program.", struct_name)
413        }
414        Ok(())
415    }
416
417    /// Adds a new record to the program.
418    ///
419    /// # Errors
420    /// This method will halt if the record was previously added.
421    /// This method will halt if the record name is already in use in the program.
422    /// This method will halt if the record name is a reserved opcode or keyword.
423    /// This method will halt if any records in the record's members are not already defined.
424    #[inline]
425    fn add_record(&mut self, record: RecordType<N>) -> Result<()> {
426        // Retrieve the record name.
427        let record_name = *record.name();
428
429        // Ensure the program has not exceeded the maximum number of records.
430        ensure!(self.records.len() < N::MAX_RECORDS, "Program exceeds the maximum number of records.");
431
432        // Ensure the record name is new.
433        ensure!(self.is_unique_name(&record_name), "'{record_name}' is already in use.");
434        // Ensure the record name is not a reserved opcode.
435        ensure!(!Self::is_reserved_opcode(&record_name.to_string()), "'{record_name}' is a reserved opcode.");
436        // Ensure the record name is not a reserved keyword.
437        ensure!(!Self::is_reserved_keyword(&record_name), "'{record_name}' is a reserved keyword.");
438
439        // Ensure all record entries are well-formed.
440        // Note: This design ensures cyclic references are not possible.
441        for (identifier, entry_type) in record.entries() {
442            // Ensure the member name is not a reserved keyword.
443            ensure!(!Self::is_reserved_keyword(identifier), "'{identifier}' is a reserved keyword.");
444            // Ensure the member type is already defined in the program.
445            match entry_type.plaintext_type() {
446                PlaintextType::Literal(_) => continue,
447                PlaintextType::Struct(identifier) => {
448                    if !self.structs.contains_key(identifier) {
449                        bail!("Struct '{identifier}' in record '{record_name}' is not defined.")
450                    }
451                }
452                PlaintextType::Array(array_type) => {
453                    if let PlaintextType::Struct(struct_name) = array_type.base_element_type() {
454                        // Ensure the member struct name exists in the program.
455                        if !self.structs.contains_key(struct_name) {
456                            bail!("'{struct_name}' in array '{array_type}' is not defined.")
457                        }
458                    }
459                }
460            }
461        }
462
463        // Add the record name to the identifiers.
464        if self.identifiers.insert(record_name, ProgramDefinition::Record).is_some() {
465            bail!("'{record_name}' already exists in the program.")
466        }
467        // Add the record to the program.
468        if self.records.insert(record_name, record).is_some() {
469            bail!("'{record_name}' already exists in the program.")
470        }
471        Ok(())
472    }
473
474    /// Adds a new closure to the program.
475    ///
476    /// # Errors
477    /// This method will halt if the closure was previously added.
478    /// This method will halt if the closure name is already in use in the program.
479    /// This method will halt if the closure name is a reserved opcode or keyword.
480    /// This method will halt if any registers are assigned more than once.
481    /// This method will halt if the registers are not incrementing monotonically.
482    /// This method will halt if an input type references a non-existent definition.
483    /// This method will halt if an operand register does not already exist in memory.
484    /// This method will halt if a destination register already exists in memory.
485    /// This method will halt if an output register does not already exist.
486    /// This method will halt if an output type references a non-existent definition.
487    #[inline]
488    fn add_closure(&mut self, closure: ClosureCore<N, Instruction>) -> Result<()> {
489        // Retrieve the closure name.
490        let closure_name = *closure.name();
491
492        // Ensure the program has not exceeded the maximum number of closures.
493        ensure!(self.closures.len() < N::MAX_CLOSURES, "Program exceeds the maximum number of closures.");
494
495        // Ensure the closure name is new.
496        ensure!(self.is_unique_name(&closure_name), "'{closure_name}' is already in use.");
497        // Ensure the closure name is not a reserved opcode.
498        ensure!(!Self::is_reserved_opcode(&closure_name.to_string()), "'{closure_name}' is a reserved opcode.");
499        // Ensure the closure name is not a reserved keyword.
500        ensure!(!Self::is_reserved_keyword(&closure_name), "'{closure_name}' is a reserved keyword.");
501
502        // Ensure there are input statements in the closure.
503        ensure!(!closure.inputs().is_empty(), "Cannot evaluate a closure without input statements");
504        // Ensure the number of inputs is within the allowed range.
505        ensure!(closure.inputs().len() <= N::MAX_INPUTS, "Closure exceeds maximum number of inputs");
506        // Ensure there are instructions in the closure.
507        ensure!(!closure.instructions().is_empty(), "Cannot evaluate a closure without instructions");
508        // Ensure the number of outputs is within the allowed range.
509        ensure!(closure.outputs().len() <= N::MAX_OUTPUTS, "Closure exceeds maximum number of outputs");
510
511        // Add the function name to the identifiers.
512        if self.identifiers.insert(closure_name, ProgramDefinition::Closure).is_some() {
513            bail!("'{closure_name}' already exists in the program.")
514        }
515        // Add the closure to the program.
516        if self.closures.insert(closure_name, closure).is_some() {
517            bail!("'{closure_name}' already exists in the program.")
518        }
519        Ok(())
520    }
521
522    /// Adds a new function to the program.
523    ///
524    /// # Errors
525    /// This method will halt if the function was previously added.
526    /// This method will halt if the function name is already in use in the program.
527    /// This method will halt if the function name is a reserved opcode or keyword.
528    /// This method will halt if any registers are assigned more than once.
529    /// This method will halt if the registers are not incrementing monotonically.
530    /// This method will halt if an input type references a non-existent definition.
531    /// This method will halt if an operand register does not already exist in memory.
532    /// This method will halt if a destination register already exists in memory.
533    /// This method will halt if an output register does not already exist.
534    /// This method will halt if an output type references a non-existent definition.
535    #[inline]
536    fn add_function(&mut self, function: FunctionCore<N, Instruction, Command>) -> Result<()> {
537        // Retrieve the function name.
538        let function_name = *function.name();
539
540        // Ensure the program has not exceeded the maximum number of functions.
541        ensure!(self.functions.len() < N::MAX_FUNCTIONS, "Program exceeds the maximum number of functions");
542
543        // Ensure the function name is new.
544        ensure!(self.is_unique_name(&function_name), "'{function_name}' is already in use.");
545        // Ensure the function name is not a reserved opcode.
546        ensure!(!Self::is_reserved_opcode(&function_name.to_string()), "'{function_name}' is a reserved opcode.");
547        // Ensure the function name is not a reserved keyword.
548        ensure!(!Self::is_reserved_keyword(&function_name), "'{function_name}' is a reserved keyword.");
549
550        // Ensure the number of inputs is within the allowed range.
551        ensure!(function.inputs().len() <= N::MAX_INPUTS, "Function exceeds maximum number of inputs");
552        // Ensure the number of instructions is within the allowed range.
553        ensure!(function.instructions().len() <= N::MAX_INSTRUCTIONS, "Function exceeds maximum instructions");
554        // Ensure the number of outputs is within the allowed range.
555        ensure!(function.outputs().len() <= N::MAX_OUTPUTS, "Function exceeds maximum number of outputs");
556
557        // Add the function name to the identifiers.
558        if self.identifiers.insert(function_name, ProgramDefinition::Function).is_some() {
559            bail!("'{function_name}' already exists in the program.")
560        }
561        // Add the function to the program.
562        if self.functions.insert(function_name, function).is_some() {
563            bail!("'{function_name}' already exists in the program.")
564        }
565        Ok(())
566    }
567}
568
569impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> ProgramCore<N, Instruction, Command> {
570    #[rustfmt::skip]
571    const KEYWORDS: &'static [&'static str] = &[
572        // Mode
573        "const",
574        "constant",
575        "public",
576        "private",
577        // Literals
578        "address",
579        "boolean",
580        "field",
581        "group",
582        "i8",
583        "i16",
584        "i32",
585        "i64",
586        "i128",
587        "u8",
588        "u16",
589        "u32",
590        "u64",
591        "u128",
592        "scalar",
593        "signature",
594        "string",
595        // Boolean
596        "true",
597        "false",
598        // Statements
599        "input",
600        "output",
601        "as",
602        "into",
603        // Record
604        "record",
605        "owner",
606        // Program
607        "transition",
608        "import",
609        "function",
610        "struct",
611        "closure",
612        "program",
613        "aleo",
614        "self",
615        "storage",
616        "mapping",
617        "key",
618        "value",
619        "async",
620        "finalize",
621        // Reserved (catch all)
622        "global",
623        "block",
624        "return",
625        "break",
626        "assert",
627        "continue",
628        "let",
629        "if",
630        "else",
631        "while",
632        "for",
633        "switch",
634        "case",
635        "default",
636        "match",
637        "enum",
638        "struct",
639        "union",
640        "trait",
641        "impl",
642        "type",
643        "future",
644    ];
645
646    /// Returns `true` if the given name does not already exist in the program.
647    fn is_unique_name(&self, name: &Identifier<N>) -> bool {
648        !self.identifiers.contains_key(name)
649    }
650
651    /// Returns `true` if the given name is a reserved opcode.
652    pub fn is_reserved_opcode(name: &str) -> bool {
653        Instruction::is_reserved_opcode(name)
654    }
655
656    /// Returns `true` if the given name uses a reserved keyword.
657    pub fn is_reserved_keyword(name: &Identifier<N>) -> bool {
658        // Convert the given name to a string.
659        let name = name.to_string();
660        // Check if the name is a keyword.
661        Self::KEYWORDS.iter().any(|keyword| *keyword == name)
662    }
663}
664
665impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> TypeName
666    for ProgramCore<N, Instruction, Command>
667{
668    /// Returns the type name as a string.
669    #[inline]
670    fn type_name() -> &'static str {
671        "program"
672    }
673}
674
675#[cfg(test)]
676mod tests {
677    use super::*;
678    use console::{
679        network::MainnetV0,
680        program::{Locator, ValueType},
681    };
682
683    type CurrentNetwork = MainnetV0;
684
685    #[test]
686    fn test_program_mapping() -> Result<()> {
687        // Create a new mapping.
688        let mapping = Mapping::<CurrentNetwork>::from_str(
689            r"
690mapping message:
691    key as field.public;
692    value as field.public;",
693        )?;
694
695        // Initialize a new program.
696        let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {mapping}"))?;
697        // Ensure the mapping was added.
698        assert!(program.contains_mapping(&Identifier::from_str("message")?));
699        // Ensure the retrieved mapping matches.
700        assert_eq!(mapping.to_string(), program.get_mapping(&Identifier::from_str("message")?)?.to_string());
701
702        Ok(())
703    }
704
705    #[test]
706    fn test_program_struct() -> Result<()> {
707        // Create a new struct.
708        let struct_ = StructType::<CurrentNetwork>::from_str(
709            r"
710struct message:
711    first as field;
712    second as field;",
713        )?;
714
715        // Initialize a new program.
716        let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {struct_}"))?;
717        // Ensure the struct was added.
718        assert!(program.contains_struct(&Identifier::from_str("message")?));
719        // Ensure the retrieved struct matches.
720        assert_eq!(&struct_, program.get_struct(&Identifier::from_str("message")?)?);
721
722        Ok(())
723    }
724
725    #[test]
726    fn test_program_record() -> Result<()> {
727        // Create a new record.
728        let record = RecordType::<CurrentNetwork>::from_str(
729            r"
730record foo:
731    owner as address.private;
732    first as field.private;
733    second as field.public;",
734        )?;
735
736        // Initialize a new program.
737        let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {record}"))?;
738        // Ensure the record was added.
739        assert!(program.contains_record(&Identifier::from_str("foo")?));
740        // Ensure the retrieved record matches.
741        assert_eq!(&record, program.get_record(&Identifier::from_str("foo")?)?);
742
743        Ok(())
744    }
745
746    #[test]
747    fn test_program_function() -> Result<()> {
748        // Create a new function.
749        let function = Function::<CurrentNetwork>::from_str(
750            r"
751function compute:
752    input r0 as field.public;
753    input r1 as field.private;
754    add r0 r1 into r2;
755    output r2 as field.private;",
756        )?;
757
758        // Initialize a new program.
759        let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {function}"))?;
760        // Ensure the function was added.
761        assert!(program.contains_function(&Identifier::from_str("compute")?));
762        // Ensure the retrieved function matches.
763        assert_eq!(function, program.get_function(&Identifier::from_str("compute")?)?);
764
765        Ok(())
766    }
767
768    #[test]
769    fn test_program_import() -> Result<()> {
770        // Initialize a new program.
771        let program = Program::<CurrentNetwork>::from_str(
772            r"
773import eth.aleo;
774import usdc.aleo;
775
776program swap.aleo;
777
778// The `swap` function transfers ownership of the record
779// for token A to the record owner of token B, and vice-versa.
780function swap:
781    // Input the record for token A.
782    input r0 as eth.aleo/eth.record;
783    // Input the record for token B.
784    input r1 as usdc.aleo/usdc.record;
785
786    // Send the record for token A to the owner of token B.
787    call eth.aleo/transfer r0 r1.owner r0.amount into r2 r3;
788
789    // Send the record for token B to the owner of token A.
790    call usdc.aleo/transfer r1 r0.owner r1.amount into r4 r5;
791
792    // Output the new record for token A.
793    output r2 as eth.aleo/eth.record;
794    // Output the new record for token B.
795    output r4 as usdc.aleo/usdc.record;
796    ",
797        )
798        .unwrap();
799
800        // Ensure the program imports exist.
801        assert!(program.contains_import(&ProgramID::from_str("eth.aleo")?));
802        assert!(program.contains_import(&ProgramID::from_str("usdc.aleo")?));
803
804        // Retrieve the 'swap' function.
805        let function = program.get_function(&Identifier::from_str("swap")?)?;
806
807        // Ensure there are two inputs.
808        assert_eq!(function.inputs().len(), 2);
809        assert_eq!(function.input_types().len(), 2);
810
811        // Declare the expected input types.
812        let expected_input_type_1 = ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?);
813        let expected_input_type_2 = ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?);
814
815        // Ensure the inputs are external records.
816        assert_eq!(function.input_types()[0], expected_input_type_1);
817        assert_eq!(function.input_types()[1], expected_input_type_2);
818
819        // Ensure the input variants are correct.
820        assert_eq!(function.input_types()[0].variant(), expected_input_type_1.variant());
821        assert_eq!(function.input_types()[1].variant(), expected_input_type_2.variant());
822
823        // Ensure there are two instructions.
824        assert_eq!(function.instructions().len(), 2);
825
826        // Ensure the instructions are calls.
827        assert_eq!(function.instructions()[0].opcode(), Opcode::Call);
828        assert_eq!(function.instructions()[1].opcode(), Opcode::Call);
829
830        // Ensure there are two outputs.
831        assert_eq!(function.outputs().len(), 2);
832        assert_eq!(function.output_types().len(), 2);
833
834        // Declare the expected output types.
835        let expected_output_type_1 = ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?);
836        let expected_output_type_2 = ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?);
837
838        // Ensure the outputs are external records.
839        assert_eq!(function.output_types()[0], expected_output_type_1);
840        assert_eq!(function.output_types()[1], expected_output_type_2);
841
842        // Ensure the output variants are correct.
843        assert_eq!(function.output_types()[0].variant(), expected_output_type_1.variant());
844        assert_eq!(function.output_types()[1].variant(), expected_output_type_2.variant());
845
846        Ok(())
847    }
848}