snarkvm_synthesizer_program/
lib.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
16#![forbid(unsafe_code)]
17#![allow(clippy::too_many_arguments)]
18#![warn(clippy::cast_possible_truncation)]
19
20extern crate snarkvm_circuit as circuit;
21extern crate snarkvm_console as console;
22
23pub type Program<N> = crate::ProgramCore<N>;
24pub type Function<N> = crate::FunctionCore<N>;
25pub type Finalize<N> = crate::FinalizeCore<N>;
26pub type Closure<N> = crate::ClosureCore<N>;
27pub type Constructor<N> = crate::ConstructorCore<N>;
28
29mod closure;
30pub use closure::*;
31
32mod constructor;
33pub use constructor::*;
34
35pub mod finalize;
36pub use finalize::*;
37
38mod function;
39pub use function::*;
40
41mod import;
42pub use import::*;
43
44pub mod logic;
45pub use logic::*;
46
47mod mapping;
48pub use mapping::*;
49
50mod traits;
51pub use traits::*;
52
53mod bytes;
54mod parse;
55mod serialize;
56mod to_checksum;
57
58use console::{
59    network::{
60        ConsensusVersion,
61        prelude::{
62            Debug,
63            Deserialize,
64            Deserializer,
65            Display,
66            Err,
67            Error,
68            ErrorKind,
69            Formatter,
70            FromBytes,
71            FromBytesDeserializer,
72            FromStr,
73            IoResult,
74            Itertools,
75            Network,
76            Parser,
77            ParserResult,
78            Read,
79            Result,
80            Sanitizer,
81            Serialize,
82            Serializer,
83            ToBytes,
84            ToBytesSerializer,
85            TypeName,
86            Write,
87            anyhow,
88            bail,
89            de,
90            ensure,
91            error,
92            fmt,
93            make_error,
94            many0,
95            many1,
96            map,
97            map_res,
98            tag,
99            take,
100        },
101    },
102    program::{Identifier, PlaintextType, ProgramID, RecordType, StructType},
103    types::U8,
104};
105use snarkvm_utilities::cfg_iter;
106
107use indexmap::{IndexMap, IndexSet};
108use std::collections::BTreeSet;
109use tiny_keccak::{Hasher, Sha3 as TinySha3};
110
111#[derive(Copy, Clone, PartialEq, Eq, Hash)]
112enum ProgramLabel<N: Network> {
113    /// A program constructor.
114    Constructor,
115    /// A named component.
116    Identifier(Identifier<N>),
117}
118
119#[cfg(not(feature = "serial"))]
120use rayon::prelude::*;
121
122#[derive(Copy, Clone, PartialEq, Eq, Hash)]
123enum ProgramDefinition {
124    /// A program constructor.
125    Constructor,
126    /// A program mapping.
127    Mapping,
128    /// A program struct.
129    Struct,
130    /// A program record.
131    Record,
132    /// A program closure.
133    Closure,
134    /// A program function.
135    Function,
136}
137
138#[derive(Clone)]
139pub struct ProgramCore<N: Network> {
140    /// The ID of the program.
141    id: ProgramID<N>,
142    /// A map of the declared imports for the program.
143    imports: IndexMap<ProgramID<N>, Import<N>>,
144    /// A map of program labels to their program definitions.
145    components: IndexMap<ProgramLabel<N>, ProgramDefinition>,
146    /// An optional constructor for the program.
147    constructor: Option<ConstructorCore<N>>,
148    /// A map of the declared mappings for the program.
149    mappings: IndexMap<Identifier<N>, Mapping<N>>,
150    /// A map of the declared structs for the program.
151    structs: IndexMap<Identifier<N>, StructType<N>>,
152    /// A map of the declared record types for the program.
153    records: IndexMap<Identifier<N>, RecordType<N>>,
154    /// A map of the declared closures for the program.
155    closures: IndexMap<Identifier<N>, ClosureCore<N>>,
156    /// A map of the declared functions for the program.
157    functions: IndexMap<Identifier<N>, FunctionCore<N>>,
158}
159
160impl<N: Network> PartialEq for ProgramCore<N> {
161    /// Compares two programs for equality, verifying that the components are in the same order.
162    /// The order of the components must match to ensure that deployment tree is well-formed.
163    fn eq(&self, other: &Self) -> bool {
164        // Check that the number of components is the same.
165        if self.components.len() != other.components.len() {
166            return false;
167        }
168        // Check that the components match in order.
169        for (left, right) in self.components.iter().zip_eq(other.components.iter()) {
170            if left != right {
171                return false;
172            }
173        }
174        // Check that the remaining fields match.
175        self.id == other.id
176            && self.imports == other.imports
177            && self.mappings == other.mappings
178            && self.structs == other.structs
179            && self.records == other.records
180            && self.closures == other.closures
181            && self.functions == other.functions
182    }
183}
184
185impl<N: Network> Eq for ProgramCore<N> {}
186
187impl<N: Network> ProgramCore<N> {
188    /// A list of reserved keywords for Aleo programs, enforced at the parser level.
189    // New keywords should be enforced through `RESTRICTED_KEYWORDS` instead, if possible.
190    // Adding keywords to this list will require a backwards-compatible versioning for programs.
191    #[rustfmt::skip]
192    pub const KEYWORDS: &'static [&'static str] = &[
193        // Mode
194        "const",
195        "constant",
196        "public",
197        "private",
198        // Literals
199        "address",
200        "boolean",
201        "field",
202        "group",
203        "i8",
204        "i16",
205        "i32",
206        "i64",
207        "i128",
208        "u8",
209        "u16",
210        "u32",
211        "u64",
212        "u128",
213        "scalar",
214        "signature",
215        "string",
216        // Boolean
217        "true",
218        "false",
219        // Statements
220        "input",
221        "output",
222        "as",
223        "into",
224        // Record
225        "record",
226        "owner",
227        // Program
228        "transition",
229        "import",
230        "function",
231        "struct",
232        "closure",
233        "program",
234        "aleo",
235        "self",
236        "storage",
237        "mapping",
238        "key",
239        "value",
240        "async",
241        "finalize",
242        // Reserved (catch all)
243        "global",
244        "block",
245        "return",
246        "break",
247        "assert",
248        "continue",
249        "let",
250        "if",
251        "else",
252        "while",
253        "for",
254        "switch",
255        "case",
256        "default",
257        "match",
258        "enum",
259        "struct",
260        "union",
261        "trait",
262        "impl",
263        "type",
264        "future",
265    ];
266    /// A list of restricted keywords for Aleo programs, enforced at the VM-level for program hygiene.
267    /// Each entry is a tuple of the consensus version and a list of keywords.
268    /// If the current consensus version is greater than or equal to the specified version,
269    /// the keywords in the list should be restricted.
270    #[rustfmt::skip]
271    pub const RESTRICTED_KEYWORDS: &'static [(ConsensusVersion, &'static [&'static str])] = &[
272        (ConsensusVersion::V6, &["constructor"])
273    ];
274
275    /// Initializes an empty program.
276    #[inline]
277    pub fn new(id: ProgramID<N>) -> Result<Self> {
278        // Ensure the program name is valid.
279        ensure!(!Self::is_reserved_keyword(id.name()), "Program name is invalid: {}", id.name());
280
281        Ok(Self {
282            id,
283            imports: IndexMap::new(),
284            constructor: None,
285            components: IndexMap::new(),
286            mappings: IndexMap::new(),
287            structs: IndexMap::new(),
288            records: IndexMap::new(),
289            closures: IndexMap::new(),
290            functions: IndexMap::new(),
291        })
292    }
293
294    /// Initializes the credits program.
295    #[inline]
296    pub fn credits() -> Result<Self> {
297        Self::from_str(include_str!("./resources/credits.aleo"))
298    }
299
300    /// Returns the ID of the program.
301    pub const fn id(&self) -> &ProgramID<N> {
302        &self.id
303    }
304
305    /// Returns the imports in the program.
306    pub const fn imports(&self) -> &IndexMap<ProgramID<N>, Import<N>> {
307        &self.imports
308    }
309
310    /// Returns the constructor for the program.
311    pub const fn constructor(&self) -> Option<&ConstructorCore<N>> {
312        self.constructor.as_ref()
313    }
314
315    /// Returns the mappings in the program.
316    pub const fn mappings(&self) -> &IndexMap<Identifier<N>, Mapping<N>> {
317        &self.mappings
318    }
319
320    /// Returns the structs in the program.
321    pub const fn structs(&self) -> &IndexMap<Identifier<N>, StructType<N>> {
322        &self.structs
323    }
324
325    /// Returns the records in the program.
326    pub const fn records(&self) -> &IndexMap<Identifier<N>, RecordType<N>> {
327        &self.records
328    }
329
330    /// Returns the closures in the program.
331    pub const fn closures(&self) -> &IndexMap<Identifier<N>, ClosureCore<N>> {
332        &self.closures
333    }
334
335    /// Returns the functions in the program.
336    pub const fn functions(&self) -> &IndexMap<Identifier<N>, FunctionCore<N>> {
337        &self.functions
338    }
339
340    /// Returns `true` if the program contains an import with the given program ID.
341    pub fn contains_import(&self, id: &ProgramID<N>) -> bool {
342        self.imports.contains_key(id)
343    }
344
345    /// Returns `true` if the program contains a constructor.
346    pub const fn contains_constructor(&self) -> bool {
347        self.constructor.is_some()
348    }
349
350    /// Returns `true` if the program contains a mapping with the given name.
351    pub fn contains_mapping(&self, name: &Identifier<N>) -> bool {
352        self.mappings.contains_key(name)
353    }
354
355    /// Returns `true` if the program contains a struct with the given name.
356    pub fn contains_struct(&self, name: &Identifier<N>) -> bool {
357        self.structs.contains_key(name)
358    }
359
360    /// Returns `true` if the program contains a record with the given name.
361    pub fn contains_record(&self, name: &Identifier<N>) -> bool {
362        self.records.contains_key(name)
363    }
364
365    /// Returns `true` if the program contains a closure with the given name.
366    pub fn contains_closure(&self, name: &Identifier<N>) -> bool {
367        self.closures.contains_key(name)
368    }
369
370    /// Returns `true` if the program contains a function with the given name.
371    pub fn contains_function(&self, name: &Identifier<N>) -> bool {
372        self.functions.contains_key(name)
373    }
374
375    /// Returns the mapping with the given name.
376    pub fn get_mapping(&self, name: &Identifier<N>) -> Result<Mapping<N>> {
377        // Attempt to retrieve the mapping.
378        let mapping = self.mappings.get(name).cloned().ok_or_else(|| anyhow!("Mapping '{name}' is not defined."))?;
379        // Ensure the mapping name matches.
380        ensure!(mapping.name() == name, "Expected mapping '{name}', but found mapping '{}'", mapping.name());
381        // Return the mapping.
382        Ok(mapping)
383    }
384
385    /// Returns the struct with the given name.
386    pub fn get_struct(&self, name: &Identifier<N>) -> Result<&StructType<N>> {
387        // Attempt to retrieve the struct.
388        let struct_ = self.structs.get(name).ok_or_else(|| anyhow!("Struct '{name}' is not defined."))?;
389        // Ensure the struct name matches.
390        ensure!(struct_.name() == name, "Expected struct '{name}', but found struct '{}'", struct_.name());
391        // Ensure the struct contains members.
392        ensure!(!struct_.members().is_empty(), "Struct '{name}' is missing members.");
393        // Return the struct.
394        Ok(struct_)
395    }
396
397    /// Returns the record with the given name.
398    pub fn get_record(&self, name: &Identifier<N>) -> Result<&RecordType<N>> {
399        // Attempt to retrieve the record.
400        let record = self.records.get(name).ok_or_else(|| anyhow!("Record '{name}' is not defined."))?;
401        // Ensure the record name matches.
402        ensure!(record.name() == name, "Expected record '{name}', but found record '{}'", record.name());
403        // Return the record.
404        Ok(record)
405    }
406
407    /// Returns the closure with the given name.
408    pub fn get_closure(&self, name: &Identifier<N>) -> Result<ClosureCore<N>> {
409        // Attempt to retrieve the closure.
410        let closure = self.closures.get(name).cloned().ok_or_else(|| anyhow!("Closure '{name}' is not defined."))?;
411        // Ensure the closure name matches.
412        ensure!(closure.name() == name, "Expected closure '{name}', but found closure '{}'", closure.name());
413        // Ensure there are input statements in the closure.
414        ensure!(!closure.inputs().is_empty(), "Cannot evaluate a closure without input statements");
415        // Ensure the number of inputs is within the allowed range.
416        ensure!(closure.inputs().len() <= N::MAX_INPUTS, "Closure exceeds maximum number of inputs");
417        // Ensure there are instructions in the closure.
418        ensure!(!closure.instructions().is_empty(), "Cannot evaluate a closure without instructions");
419        // Ensure the number of outputs is within the allowed range.
420        ensure!(closure.outputs().len() <= N::MAX_OUTPUTS, "Closure exceeds maximum number of outputs");
421        // Return the closure.
422        Ok(closure)
423    }
424
425    /// Returns the function with the given name.
426    pub fn get_function(&self, name: &Identifier<N>) -> Result<FunctionCore<N>> {
427        self.get_function_ref(name).cloned()
428    }
429
430    /// Returns a reference to the function with the given name.
431    pub fn get_function_ref(&self, name: &Identifier<N>) -> Result<&FunctionCore<N>> {
432        // Attempt to retrieve the function.
433        let function = self.functions.get(name).ok_or(anyhow!("Function '{}/{name}' is not defined.", self.id))?;
434        // Ensure the function name matches.
435        ensure!(function.name() == name, "Expected function '{name}', but found function '{}'", function.name());
436        // Ensure the number of inputs is within the allowed range.
437        ensure!(function.inputs().len() <= N::MAX_INPUTS, "Function exceeds maximum number of inputs");
438        // Ensure the number of instructions is within the allowed range.
439        ensure!(function.instructions().len() <= N::MAX_INSTRUCTIONS, "Function exceeds maximum instructions");
440        // Ensure the number of outputs is within the allowed range.
441        ensure!(function.outputs().len() <= N::MAX_OUTPUTS, "Function exceeds maximum number of outputs");
442        // Return the function.
443        Ok(function)
444    }
445
446    /// Adds a new import statement to the program.
447    ///
448    /// # Errors
449    /// This method will halt if the imported program was previously added.
450    #[inline]
451    fn add_import(&mut self, import: Import<N>) -> Result<()> {
452        // Retrieve the imported program name.
453        let import_name = *import.name();
454
455        // Ensure that the number of imports is within the allowed range.
456        ensure!(self.imports.len() < N::MAX_IMPORTS, "Program exceeds the maximum number of imports");
457
458        // Ensure the import name is new.
459        ensure!(self.is_unique_name(&import_name), "'{import_name}' is already in use.");
460        // Ensure the import name is not a reserved opcode.
461        ensure!(!Self::is_reserved_opcode(&import_name.to_string()), "'{import_name}' is a reserved opcode.");
462        // Ensure the import name is not a reserved keyword.
463        ensure!(!Self::is_reserved_keyword(&import_name), "'{import_name}' is a reserved keyword.");
464
465        // Ensure the import is new.
466        ensure!(
467            !self.imports.contains_key(import.program_id()),
468            "Import '{}' is already defined.",
469            import.program_id()
470        );
471
472        // Add the import statement to the program.
473        if self.imports.insert(*import.program_id(), import.clone()).is_some() {
474            bail!("'{}' already exists in the program.", import.program_id())
475        }
476        Ok(())
477    }
478
479    /// Adds a constructor to the program.
480    ///
481    /// # Errors
482    /// This method will halt if a constructor was previously added.
483    /// This method will halt if a constructor exceeds the maximum number of commands.
484    fn add_constructor(&mut self, constructor: ConstructorCore<N>) -> Result<()> {
485        // Ensure the program does not already have a constructor.
486        ensure!(self.constructor.is_none(), "Program already has a constructor.");
487        // Ensure the number of commands is within the allowed range.
488        ensure!(!constructor.commands().is_empty(), "Constructor must have at least one command");
489        ensure!(constructor.commands().len() <= N::MAX_COMMANDS, "Constructor exceeds maximum number of commands");
490        // Add the constructor to the components.
491        if self.components.insert(ProgramLabel::Constructor, ProgramDefinition::Constructor).is_some() {
492            bail!("Constructor already exists in the program.")
493        }
494        // Add the constructor to the program.
495        self.constructor = Some(constructor);
496        Ok(())
497    }
498
499    /// Adds a new mapping to the program.
500    ///
501    /// # Errors
502    /// This method will halt if the mapping name is already in use.
503    /// This method will halt if the mapping name is a reserved opcode or keyword.
504    #[inline]
505    fn add_mapping(&mut self, mapping: Mapping<N>) -> Result<()> {
506        // Retrieve the mapping name.
507        let mapping_name = *mapping.name();
508
509        // Ensure the program has not exceeded the maximum number of mappings.
510        ensure!(self.mappings.len() < N::MAX_MAPPINGS, "Program exceeds the maximum number of mappings");
511
512        // Ensure the mapping name is new.
513        ensure!(self.is_unique_name(&mapping_name), "'{mapping_name}' is already in use.");
514        // Ensure the mapping name is not a reserved keyword.
515        ensure!(!Self::is_reserved_keyword(&mapping_name), "'{mapping_name}' is a reserved keyword.");
516        // Ensure the mapping name is not a reserved opcode.
517        ensure!(!Self::is_reserved_opcode(&mapping_name.to_string()), "'{mapping_name}' is a reserved opcode.");
518
519        // Add the mapping name to the identifiers.
520        if self.components.insert(ProgramLabel::Identifier(mapping_name), ProgramDefinition::Mapping).is_some() {
521            bail!("'{mapping_name}' already exists in the program.")
522        }
523        // Add the mapping to the program.
524        if self.mappings.insert(mapping_name, mapping).is_some() {
525            bail!("'{mapping_name}' already exists in the program.")
526        }
527        Ok(())
528    }
529
530    /// Adds a new struct to the program.
531    ///
532    /// # Errors
533    /// This method will halt if the struct was previously added.
534    /// This method will halt if the struct name is already in use in the program.
535    /// This method will halt if the struct name is a reserved opcode or keyword.
536    /// This method will halt if any structs in the struct's members are not already defined.
537    #[inline]
538    fn add_struct(&mut self, struct_: StructType<N>) -> Result<()> {
539        // Retrieve the struct name.
540        let struct_name = *struct_.name();
541
542        // Ensure the program has not exceeded the maximum number of structs.
543        ensure!(self.structs.len() < N::MAX_STRUCTS, "Program exceeds the maximum number of structs.");
544
545        // Ensure the struct name is new.
546        ensure!(self.is_unique_name(&struct_name), "'{struct_name}' is already in use.");
547        // Ensure the struct name is not a reserved opcode.
548        ensure!(!Self::is_reserved_opcode(&struct_name.to_string()), "'{struct_name}' is a reserved opcode.");
549        // Ensure the struct name is not a reserved keyword.
550        ensure!(!Self::is_reserved_keyword(&struct_name), "'{struct_name}' is a reserved keyword.");
551
552        // Ensure the struct contains members.
553        ensure!(!struct_.members().is_empty(), "Struct '{struct_name}' is missing members.");
554
555        // Ensure all struct members are well-formed.
556        // Note: This design ensures cyclic references are not possible.
557        for (identifier, plaintext_type) in struct_.members() {
558            // Ensure the member name is not a reserved keyword.
559            ensure!(!Self::is_reserved_keyword(identifier), "'{identifier}' is a reserved keyword.");
560            // Ensure the member type is already defined in the program.
561            match plaintext_type {
562                PlaintextType::Literal(_) => continue,
563                PlaintextType::Struct(member_identifier) => {
564                    // Ensure the member struct name exists in the program.
565                    if !self.structs.contains_key(member_identifier) {
566                        bail!("'{member_identifier}' in struct '{struct_name}' is not defined.")
567                    }
568                }
569                PlaintextType::Array(array_type) => {
570                    if let PlaintextType::Struct(struct_name) = array_type.base_element_type() {
571                        // Ensure the member struct name exists in the program.
572                        if !self.structs.contains_key(struct_name) {
573                            bail!("'{struct_name}' in array '{array_type}' is not defined.")
574                        }
575                    }
576                }
577            }
578        }
579
580        // Add the struct name to the identifiers.
581        if self.components.insert(ProgramLabel::Identifier(struct_name), ProgramDefinition::Struct).is_some() {
582            bail!("'{struct_name}' already exists in the program.")
583        }
584        // Add the struct to the program.
585        if self.structs.insert(struct_name, struct_).is_some() {
586            bail!("'{struct_name}' already exists in the program.")
587        }
588        Ok(())
589    }
590
591    /// Adds a new record to the program.
592    ///
593    /// # Errors
594    /// This method will halt if the record was previously added.
595    /// This method will halt if the record name is already in use in the program.
596    /// This method will halt if the record name is a reserved opcode or keyword.
597    /// This method will halt if any records in the record's members are not already defined.
598    #[inline]
599    fn add_record(&mut self, record: RecordType<N>) -> Result<()> {
600        // Retrieve the record name.
601        let record_name = *record.name();
602
603        // Ensure the program has not exceeded the maximum number of records.
604        ensure!(self.records.len() < N::MAX_RECORDS, "Program exceeds the maximum number of records.");
605
606        // Ensure the record name is new.
607        ensure!(self.is_unique_name(&record_name), "'{record_name}' is already in use.");
608        // Ensure the record name is not a reserved opcode.
609        ensure!(!Self::is_reserved_opcode(&record_name.to_string()), "'{record_name}' is a reserved opcode.");
610        // Ensure the record name is not a reserved keyword.
611        ensure!(!Self::is_reserved_keyword(&record_name), "'{record_name}' is a reserved keyword.");
612
613        // Ensure all record entries are well-formed.
614        // Note: This design ensures cyclic references are not possible.
615        for (identifier, entry_type) in record.entries() {
616            // Ensure the member name is not a reserved keyword.
617            ensure!(!Self::is_reserved_keyword(identifier), "'{identifier}' is a reserved keyword.");
618            // Ensure the member type is already defined in the program.
619            match entry_type.plaintext_type() {
620                PlaintextType::Literal(_) => continue,
621                PlaintextType::Struct(identifier) => {
622                    if !self.structs.contains_key(identifier) {
623                        bail!("Struct '{identifier}' in record '{record_name}' is not defined.")
624                    }
625                }
626                PlaintextType::Array(array_type) => {
627                    if let PlaintextType::Struct(struct_name) = array_type.base_element_type() {
628                        // Ensure the member struct name exists in the program.
629                        if !self.structs.contains_key(struct_name) {
630                            bail!("'{struct_name}' in array '{array_type}' is not defined.")
631                        }
632                    }
633                }
634            }
635        }
636
637        // Add the record name to the identifiers.
638        if self.components.insert(ProgramLabel::Identifier(record_name), ProgramDefinition::Record).is_some() {
639            bail!("'{record_name}' already exists in the program.")
640        }
641        // Add the record to the program.
642        if self.records.insert(record_name, record).is_some() {
643            bail!("'{record_name}' already exists in the program.")
644        }
645        Ok(())
646    }
647
648    /// Adds a new closure to the program.
649    ///
650    /// # Errors
651    /// This method will halt if the closure was previously added.
652    /// This method will halt if the closure name is already in use in the program.
653    /// This method will halt if the closure name is a reserved opcode or keyword.
654    /// This method will halt if any registers are assigned more than once.
655    /// This method will halt if the registers are not incrementing monotonically.
656    /// This method will halt if an input type references a non-existent definition.
657    /// This method will halt if an operand register does not already exist in memory.
658    /// This method will halt if a destination register already exists in memory.
659    /// This method will halt if an output register does not already exist.
660    /// This method will halt if an output type references a non-existent definition.
661    #[inline]
662    fn add_closure(&mut self, closure: ClosureCore<N>) -> Result<()> {
663        // Retrieve the closure name.
664        let closure_name = *closure.name();
665
666        // Ensure the program has not exceeded the maximum number of closures.
667        ensure!(self.closures.len() < N::MAX_CLOSURES, "Program exceeds the maximum number of closures.");
668
669        // Ensure the closure name is new.
670        ensure!(self.is_unique_name(&closure_name), "'{closure_name}' is already in use.");
671        // Ensure the closure name is not a reserved opcode.
672        ensure!(!Self::is_reserved_opcode(&closure_name.to_string()), "'{closure_name}' is a reserved opcode.");
673        // Ensure the closure name is not a reserved keyword.
674        ensure!(!Self::is_reserved_keyword(&closure_name), "'{closure_name}' is a reserved keyword.");
675
676        // Ensure there are input statements in the closure.
677        ensure!(!closure.inputs().is_empty(), "Cannot evaluate a closure without input statements");
678        // Ensure the number of inputs is within the allowed range.
679        ensure!(closure.inputs().len() <= N::MAX_INPUTS, "Closure exceeds maximum number of inputs");
680        // Ensure there are instructions in the closure.
681        ensure!(!closure.instructions().is_empty(), "Cannot evaluate a closure without instructions");
682        // Ensure the number of outputs is within the allowed range.
683        ensure!(closure.outputs().len() <= N::MAX_OUTPUTS, "Closure exceeds maximum number of outputs");
684
685        // Add the function name to the identifiers.
686        if self.components.insert(ProgramLabel::Identifier(closure_name), ProgramDefinition::Closure).is_some() {
687            bail!("'{closure_name}' already exists in the program.")
688        }
689        // Add the closure to the program.
690        if self.closures.insert(closure_name, closure).is_some() {
691            bail!("'{closure_name}' already exists in the program.")
692        }
693        Ok(())
694    }
695
696    /// Adds a new function to the program.
697    ///
698    /// # Errors
699    /// This method will halt if the function was previously added.
700    /// This method will halt if the function name is already in use in the program.
701    /// This method will halt if the function name is a reserved opcode or keyword.
702    /// This method will halt if any registers are assigned more than once.
703    /// This method will halt if the registers are not incrementing monotonically.
704    /// This method will halt if an input type references a non-existent definition.
705    /// This method will halt if an operand register does not already exist in memory.
706    /// This method will halt if a destination register already exists in memory.
707    /// This method will halt if an output register does not already exist.
708    /// This method will halt if an output type references a non-existent definition.
709    #[inline]
710    fn add_function(&mut self, function: FunctionCore<N>) -> Result<()> {
711        // Retrieve the function name.
712        let function_name = *function.name();
713
714        // Ensure the program has not exceeded the maximum number of functions.
715        ensure!(self.functions.len() < N::MAX_FUNCTIONS, "Program exceeds the maximum number of functions");
716
717        // Ensure the function name is new.
718        ensure!(self.is_unique_name(&function_name), "'{function_name}' is already in use.");
719        // Ensure the function name is not a reserved opcode.
720        ensure!(!Self::is_reserved_opcode(&function_name.to_string()), "'{function_name}' is a reserved opcode.");
721        // Ensure the function name is not a reserved keyword.
722        ensure!(!Self::is_reserved_keyword(&function_name), "'{function_name}' is a reserved keyword.");
723
724        // Ensure the number of inputs is within the allowed range.
725        ensure!(function.inputs().len() <= N::MAX_INPUTS, "Function exceeds maximum number of inputs");
726        // Ensure the number of instructions is within the allowed range.
727        ensure!(function.instructions().len() <= N::MAX_INSTRUCTIONS, "Function exceeds maximum instructions");
728        // Ensure the number of outputs is within the allowed range.
729        ensure!(function.outputs().len() <= N::MAX_OUTPUTS, "Function exceeds maximum number of outputs");
730
731        // Add the function name to the identifiers.
732        if self.components.insert(ProgramLabel::Identifier(function_name), ProgramDefinition::Function).is_some() {
733            bail!("'{function_name}' already exists in the program.")
734        }
735        // Add the function to the program.
736        if self.functions.insert(function_name, function).is_some() {
737            bail!("'{function_name}' already exists in the program.")
738        }
739        Ok(())
740    }
741
742    /// Returns `true` if the given name does not already exist in the program.
743    fn is_unique_name(&self, name: &Identifier<N>) -> bool {
744        !self.components.contains_key(&ProgramLabel::Identifier(*name))
745    }
746
747    /// Returns `true` if the given name is a reserved opcode.
748    pub fn is_reserved_opcode(name: &str) -> bool {
749        Instruction::<N>::is_reserved_opcode(name)
750    }
751
752    /// Returns `true` if the given name uses a reserved keyword.
753    pub fn is_reserved_keyword(name: &Identifier<N>) -> bool {
754        // Convert the given name to a string.
755        let name = name.to_string();
756        // Check if the name is a keyword.
757        Self::KEYWORDS.iter().any(|keyword| *keyword == name)
758    }
759
760    /// Returns an iterator over the restricted keywords for the given consensus version.
761    pub fn restricted_keywords_for_consensus_version(
762        consensus_version: ConsensusVersion,
763    ) -> impl Iterator<Item = &'static str> {
764        Self::RESTRICTED_KEYWORDS
765            .iter()
766            .filter(move |(version, _)| *version <= consensus_version)
767            .flat_map(|(_, keywords)| *keywords)
768            .copied()
769    }
770
771    /// Checks a program for restricted keywords for the given consensus version.
772    /// Returns an error if any restricted keywords are found.
773    /// Note: Restrictions are not enforced on the import names in case they were deployed before the restrictions were added.
774    pub fn check_restricted_keywords_for_consensus_version(&self, consensus_version: ConsensusVersion) -> Result<()> {
775        // Get all keywords that are restricted for the consensus version.
776        let keywords =
777            Program::<N>::restricted_keywords_for_consensus_version(consensus_version).collect::<IndexSet<_>>();
778        // Check if the program name is a restricted keywords.
779        let program_name = self.id().name().to_string();
780        if keywords.contains(&program_name.as_str()) {
781            bail!("Program name '{program_name}' is a restricted keyword for the current consensus version")
782        }
783        // Check that all top-level program components are not restricted keywords.
784        for component in self.components.keys() {
785            match component {
786                ProgramLabel::Identifier(identifier) => {
787                    if keywords.contains(identifier.to_string().as_str()) {
788                        bail!(
789                            "Program component '{identifier}' is a restricted keyword for the current consensus version"
790                        )
791                    }
792                }
793                ProgramLabel::Constructor => continue,
794            }
795        }
796        // Check that all record entry names are not restricted keywords.
797        for record_type in self.records().values() {
798            for entry_name in record_type.entries().keys() {
799                if keywords.contains(entry_name.to_string().as_str()) {
800                    bail!("Record entry '{entry_name}' is a restricted keyword for the current consensus version")
801                }
802            }
803        }
804        // Check that all struct member names are not restricted keywords.
805        for struct_type in self.structs().values() {
806            for member_name in struct_type.members().keys() {
807                if keywords.contains(member_name.to_string().as_str()) {
808                    bail!("Struct member '{member_name}' is a restricted keyword for the current consensus version")
809                }
810            }
811        }
812        // Check that all `finalize` positions.
813        // Note: It is sufficient to only check the positions in `FinalizeCore` since `FinalizeTypes::initialize` checks that every
814        // `Branch` instruction targets a valid position.
815        for function in self.functions().values() {
816            if let Some(finalize_logic) = function.finalize_logic() {
817                for position in finalize_logic.positions().keys() {
818                    if keywords.contains(position.to_string().as_str()) {
819                        bail!(
820                            "Finalize position '{position}' is a restricted keyword for the current consensus version"
821                        )
822                    }
823                }
824            }
825        }
826        Ok(())
827    }
828
829    /// Checks that the program structure is well-formed under the following rules:
830    ///  1. The program ID must not contain the keyword "aleo" in the program name.
831    ///  2. The record name must not contain the keyword "aleo".
832    ///  3. Record names must not be prefixes of other record names.
833    ///  4. Record entry names must not contain the keyword "aleo".
834    pub fn check_program_naming_structure(&self) -> Result<()> {
835        // 1. Check if the program ID contains the "aleo" substring
836        let program_id = self.id().name().to_string();
837        if program_id.contains("aleo") {
838            bail!("Program ID '{program_id}' can't contain the reserved keyword 'aleo'.");
839        }
840
841        // Fetch the record names in a sorted BTreeSet.
842        let record_names: BTreeSet<String> = self.records.keys().map(|name| name.to_string()).collect();
843
844        // 2. Check if any record name contains the "aleo" substring.
845        for record_name in &record_names {
846            if record_name.contains("aleo") {
847                bail!("Record name '{record_name}' can't contain the reserved keyword 'aleo'.");
848            }
849        }
850
851        // 3. Check if any of the record names are a prefix of another.
852        let mut record_names_iter = record_names.iter();
853        let mut previous_record_name = record_names_iter.next();
854        for record_name in record_names_iter {
855            if let Some(previous) = previous_record_name {
856                if record_name.starts_with(previous) {
857                    bail!("Record name '{previous}' can't be a prefix of record name '{record_name}'.");
858                }
859            }
860            previous_record_name = Some(record_name);
861        }
862
863        // 4. Check if any record entry names contain the "aleo" substring.
864        for record_entry_name in self.records.values().flat_map(|record_type| record_type.entries().keys()) {
865            if record_entry_name.to_string().contains("aleo") {
866                bail!("Record entry name '{record_entry_name}' can't contain the reserved keyword 'aleo'.");
867            }
868        }
869
870        Ok(())
871    }
872
873    /// Checks that the program does not make external calls to `credits.aleo/upgrade`.
874    pub fn check_external_calls_to_credits_upgrade(&self) -> Result<()> {
875        // Check if the program makes external calls to `credits.aleo/upgrade`.
876        cfg_iter!(self.functions()).flat_map(|(_, function)| function.instructions()).try_for_each(|instruction| {
877            if let Some(CallOperator::Locator(locator)) = instruction.call_operator() {
878                // Check if the locator is restricted.
879                if locator.to_string() == "credits.aleo/upgrade" {
880                    bail!("External call to restricted locator '{locator}'")
881                }
882            }
883            Ok(())
884        })?;
885        Ok(())
886    }
887
888    /// Returns `true` if a program contains any V9 syntax.
889    /// This includes `constructor`, `Operand::Edition`, `Operand::Checksum`, and `Operand::ProgramOwner`.
890    /// This is enforced to be `false` for programs before `ConsensusVersion::V9`.
891    #[inline]
892    pub fn contains_v9_syntax(&self) -> bool {
893        // Check if the program contains a constructor.
894        if self.contains_constructor() {
895            return true;
896        }
897        // Check each instruction and output in each function's finalize scope for the use of
898        // `Operand::Checksum`, `Operand::Edition` or `Operand::ProgramOwner`.
899        for function in self.functions().values() {
900            // Check the finalize scope if it exists.
901            if let Some(finalize_logic) = function.finalize_logic() {
902                // Check the command operands.
903                for command in finalize_logic.commands() {
904                    for operand in command.operands() {
905                        if matches!(operand, Operand::Checksum(_) | Operand::Edition(_) | Operand::ProgramOwner(_)) {
906                            return true;
907                        }
908                    }
909                }
910            }
911        }
912        // Return `false` since no V9 syntax was found.
913        false
914    }
915
916    /// Returns `true` if the program contains an array type with a size that exceeds the given maximum.
917    pub fn exceeds_max_array_size(&self, max_array_size: u32) -> bool {
918        self.mappings.values().any(|mapping| mapping.exceeds_max_array_size(max_array_size))
919            || self.structs.values().any(|struct_type| struct_type.exceeds_max_array_size(max_array_size))
920            || self.records.values().any(|record_type| record_type.exceeds_max_array_size(max_array_size))
921            || self.closures.values().any(|closure| closure.exceeds_max_array_size(max_array_size))
922            || self.functions.values().any(|function| function.exceeds_max_array_size(max_array_size))
923            || self.constructor.iter().any(|constructor| constructor.exceeds_max_array_size(max_array_size))
924    }
925
926    /// Returns `true` if a program contains any V11 syntax.
927    /// This includes:
928    /// 1. `.raw` hash or signature verification variants
929    /// 2. `ecdsa.verify.*` opcodes
930    /// 3. arrays that exceed the previous maximum length of 32.
931    #[inline]
932    pub fn contains_v11_syntax(&self) -> bool {
933        // The previous maximum array size before V11.
934        const V10_MAX_ARRAY_ELEMENTS: u32 = 32;
935
936        // Helper to check if any of the opcodes:
937        // - start with `ecdsa.verify`, `serialize`, or `deserialize`
938        // - end with `.raw` or `.native`
939        let has_op = |opcode: &str| {
940            opcode.starts_with("ecdsa.verify")
941                || opcode.starts_with("serialize")
942                || opcode.starts_with("deserialize")
943                || opcode.ends_with(".raw")
944                || opcode.ends_with(".native")
945        };
946
947        // Determine if any function instructions contain the new syntax.
948        let function_contains = cfg_iter!(self.functions())
949            .flat_map(|(_, function)| function.instructions())
950            .any(|instruction| has_op(*instruction.opcode()));
951
952        // Determine if any closure instructions contain the new syntax.
953        let closure_contains = cfg_iter!(self.closures())
954            .flat_map(|(_, closure)| closure.instructions())
955            .any(|instruction| has_op(*instruction.opcode()));
956
957        // Determine if any finalize commands or constructor commands contain the new syntax.
958        let command_contains = cfg_iter!(self.functions())
959            .flat_map(|(_, function)| function.finalize_logic().map(|finalize| finalize.commands()))
960            .flatten()
961            .chain(cfg_iter!(self.constructor).flat_map(|constructor| constructor.commands()))
962            .any(|command| matches!(command, Command::Instruction(instruction) if has_op(*instruction.opcode())));
963
964        // Determine if any of the array types exceed the previous maximum length of 32.
965        let array_size_exceeds = self.exceeds_max_array_size(V10_MAX_ARRAY_ELEMENTS);
966
967        function_contains || closure_contains || command_contains || array_size_exceeds
968    }
969
970    /// Returns `true` if a program contains any V12 syntax.
971    /// This includes `Operand::BlockTimestamp`.
972    /// This is enforced to be `false` for programs before `ConsensusVersion::V12`.
973    #[inline]
974    pub fn contains_v12_syntax(&self) -> bool {
975        // Check each instruction and output in each function's finalize scope for the use of
976        // `Operand::BlockTimestamp`.
977        cfg_iter!(self.functions()).any(|(_, function)| {
978            function.finalize_logic().is_some_and(|finalize_logic| {
979                cfg_iter!(finalize_logic.commands()).any(|command| {
980                    cfg_iter!(command.operands()).any(|operand| matches!(operand, Operand::BlockTimestamp))
981                })
982            })
983        })
984    }
985
986    /// Returns `true` if a program contains any string type.
987    /// Before ConsensusVersion::V12, variable-length string sampling when using them as inputs caused deployment synthesis to be inconsistent and abort with probability 63/64.
988    /// After ConsensusVersion::V12, string types are disallowed.
989    #[inline]
990    pub fn contains_string_type(&self) -> bool {
991        self.mappings.values().any(|mapping| mapping.contains_string_type())
992            || self.structs.values().any(|struct_type| struct_type.contains_string_type())
993            || self.records.values().any(|record_type| record_type.contains_string_type())
994            || self.closures.values().any(|closure| closure.contains_string_type())
995            || self.functions.values().any(|function| function.contains_string_type())
996            || self.constructor.iter().any(|constructor| constructor.contains_string_type())
997    }
998}
999
1000impl<N: Network> TypeName for ProgramCore<N> {
1001    /// Returns the type name as a string.
1002    #[inline]
1003    fn type_name() -> &'static str {
1004        "program"
1005    }
1006}
1007
1008#[cfg(test)]
1009mod tests {
1010    use super::*;
1011    use console::{
1012        network::MainnetV0,
1013        program::{Locator, ValueType},
1014    };
1015
1016    type CurrentNetwork = MainnetV0;
1017
1018    #[test]
1019    fn test_program_mapping() -> Result<()> {
1020        // Create a new mapping.
1021        let mapping = Mapping::<CurrentNetwork>::from_str(
1022            r"
1023mapping message:
1024    key as field.public;
1025    value as field.public;",
1026        )?;
1027
1028        // Initialize a new program.
1029        let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {mapping}"))?;
1030        // Ensure the mapping was added.
1031        assert!(program.contains_mapping(&Identifier::from_str("message")?));
1032        // Ensure the retrieved mapping matches.
1033        assert_eq!(mapping.to_string(), program.get_mapping(&Identifier::from_str("message")?)?.to_string());
1034
1035        Ok(())
1036    }
1037
1038    #[test]
1039    fn test_program_struct() -> Result<()> {
1040        // Create a new struct.
1041        let struct_ = StructType::<CurrentNetwork>::from_str(
1042            r"
1043struct message:
1044    first as field;
1045    second as field;",
1046        )?;
1047
1048        // Initialize a new program.
1049        let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {struct_}"))?;
1050        // Ensure the struct was added.
1051        assert!(program.contains_struct(&Identifier::from_str("message")?));
1052        // Ensure the retrieved struct matches.
1053        assert_eq!(&struct_, program.get_struct(&Identifier::from_str("message")?)?);
1054
1055        Ok(())
1056    }
1057
1058    #[test]
1059    fn test_program_record() -> Result<()> {
1060        // Create a new record.
1061        let record = RecordType::<CurrentNetwork>::from_str(
1062            r"
1063record foo:
1064    owner as address.private;
1065    first as field.private;
1066    second as field.public;",
1067        )?;
1068
1069        // Initialize a new program.
1070        let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {record}"))?;
1071        // Ensure the record was added.
1072        assert!(program.contains_record(&Identifier::from_str("foo")?));
1073        // Ensure the retrieved record matches.
1074        assert_eq!(&record, program.get_record(&Identifier::from_str("foo")?)?);
1075
1076        Ok(())
1077    }
1078
1079    #[test]
1080    fn test_program_function() -> Result<()> {
1081        // Create a new function.
1082        let function = Function::<CurrentNetwork>::from_str(
1083            r"
1084function compute:
1085    input r0 as field.public;
1086    input r1 as field.private;
1087    add r0 r1 into r2;
1088    output r2 as field.private;",
1089        )?;
1090
1091        // Initialize a new program.
1092        let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {function}"))?;
1093        // Ensure the function was added.
1094        assert!(program.contains_function(&Identifier::from_str("compute")?));
1095        // Ensure the retrieved function matches.
1096        assert_eq!(function, program.get_function(&Identifier::from_str("compute")?)?);
1097
1098        Ok(())
1099    }
1100
1101    #[test]
1102    fn test_program_import() -> Result<()> {
1103        // Initialize a new program.
1104        let program = Program::<CurrentNetwork>::from_str(
1105            r"
1106import eth.aleo;
1107import usdc.aleo;
1108
1109program swap.aleo;
1110
1111// The `swap` function transfers ownership of the record
1112// for token A to the record owner of token B, and vice-versa.
1113function swap:
1114    // Input the record for token A.
1115    input r0 as eth.aleo/eth.record;
1116    // Input the record for token B.
1117    input r1 as usdc.aleo/usdc.record;
1118
1119    // Send the record for token A to the owner of token B.
1120    call eth.aleo/transfer r0 r1.owner r0.amount into r2 r3;
1121
1122    // Send the record for token B to the owner of token A.
1123    call usdc.aleo/transfer r1 r0.owner r1.amount into r4 r5;
1124
1125    // Output the new record for token A.
1126    output r2 as eth.aleo/eth.record;
1127    // Output the new record for token B.
1128    output r4 as usdc.aleo/usdc.record;
1129    ",
1130        )
1131        .unwrap();
1132
1133        // Ensure the program imports exist.
1134        assert!(program.contains_import(&ProgramID::from_str("eth.aleo")?));
1135        assert!(program.contains_import(&ProgramID::from_str("usdc.aleo")?));
1136
1137        // Retrieve the 'swap' function.
1138        let function = program.get_function(&Identifier::from_str("swap")?)?;
1139
1140        // Ensure there are two inputs.
1141        assert_eq!(function.inputs().len(), 2);
1142        assert_eq!(function.input_types().len(), 2);
1143
1144        // Declare the expected input types.
1145        let expected_input_type_1 = ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?);
1146        let expected_input_type_2 = ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?);
1147
1148        // Ensure the inputs are external records.
1149        assert_eq!(function.input_types()[0], expected_input_type_1);
1150        assert_eq!(function.input_types()[1], expected_input_type_2);
1151
1152        // Ensure the input variants are correct.
1153        assert_eq!(function.input_types()[0].variant(), expected_input_type_1.variant());
1154        assert_eq!(function.input_types()[1].variant(), expected_input_type_2.variant());
1155
1156        // Ensure there are two instructions.
1157        assert_eq!(function.instructions().len(), 2);
1158
1159        // Ensure the instructions are calls.
1160        assert_eq!(function.instructions()[0].opcode(), Opcode::Call);
1161        assert_eq!(function.instructions()[1].opcode(), Opcode::Call);
1162
1163        // Ensure there are two outputs.
1164        assert_eq!(function.outputs().len(), 2);
1165        assert_eq!(function.output_types().len(), 2);
1166
1167        // Declare the expected output types.
1168        let expected_output_type_1 = ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?);
1169        let expected_output_type_2 = ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?);
1170
1171        // Ensure the outputs are external records.
1172        assert_eq!(function.output_types()[0], expected_output_type_1);
1173        assert_eq!(function.output_types()[1], expected_output_type_2);
1174
1175        // Ensure the output variants are correct.
1176        assert_eq!(function.output_types()[0].variant(), expected_output_type_1.variant());
1177        assert_eq!(function.output_types()[1].variant(), expected_output_type_2.variant());
1178
1179        Ok(())
1180    }
1181
1182    #[test]
1183    fn test_program_with_constructor() {
1184        // Initialize a new program.
1185        let program_string = r"import credits.aleo;
1186
1187program good_constructor.aleo;
1188
1189constructor:
1190    assert.eq edition 0u16;
1191    assert.eq credits.aleo/edition 0u16;
1192    assert.neq checksum 0field;
1193    assert.eq credits.aleo/checksum 6192738754253668739186185034243585975029374333074931926190215457304721124008field;
1194    set 1u8 into data[0u8];
1195
1196mapping data:
1197    key as u8.public;
1198    value as u8.public;
1199
1200function dummy:
1201
1202function check:
1203    async check into r0;
1204    output r0 as good_constructor.aleo/check.future;
1205
1206finalize check:
1207    get data[0u8] into r0;
1208    assert.eq r0 1u8;
1209";
1210        let program = Program::<CurrentNetwork>::from_str(program_string).unwrap();
1211
1212        // Check that the string and bytes (de)serialization works.
1213        let serialized = program.to_string();
1214        let deserialized = Program::<CurrentNetwork>::from_str(&serialized).unwrap();
1215        assert_eq!(program, deserialized);
1216
1217        let serialized = program.to_bytes_le().unwrap();
1218        let deserialized = Program::<CurrentNetwork>::from_bytes_le(&serialized).unwrap();
1219        assert_eq!(program, deserialized);
1220
1221        // Check that the display works.
1222        let display = format!("{program}");
1223        assert_eq!(display, program_string);
1224
1225        // Ensure the program contains a constructor.
1226        assert!(program.contains_constructor());
1227        assert_eq!(program.constructor().unwrap().commands().len(), 5);
1228    }
1229
1230    #[test]
1231    fn test_program_equality_and_checksum() {
1232        fn run_test(program1: &str, program2: &str, expected_equal: bool) {
1233            println!("Comparing programs:\n{program1}\n{program2}");
1234            let program1 = Program::<CurrentNetwork>::from_str(program1).unwrap();
1235            let program2 = Program::<CurrentNetwork>::from_str(program2).unwrap();
1236            assert_eq!(program1 == program2, expected_equal);
1237            assert_eq!(program1.to_checksum() == program2.to_checksum(), expected_equal);
1238        }
1239
1240        // Test two identical programs, with different whitespace.
1241        run_test(r"program test.aleo; function dummy:    ", r"program  test.aleo;     function dummy:   ", true);
1242
1243        // Test two programs, one with a different function name.
1244        run_test(r"program test.aleo; function dummy:    ", r"program test.aleo; function bummy:   ", false);
1245
1246        // Test two programs, one with a constructor and one without.
1247        run_test(
1248            r"program test.aleo; function dummy:    ",
1249            r"program test.aleo; constructor: assert.eq true true; function dummy: ",
1250            false,
1251        );
1252
1253        // Test two programs, both with a struct and function, but in different order.
1254        run_test(
1255            r"program test.aleo; struct foo: data as u8; function dummy:",
1256            r"program test.aleo; function dummy: struct foo: data as u8;",
1257            false,
1258        );
1259    }
1260}