Skip to main content

snarkvm_synthesizer_program/
lib.rs

1// Copyright (c) 2019-2026 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
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 view;
45pub use view::ViewCore;
46
47pub type View<N> = crate::ViewCore<N>;
48
49pub mod logic;
50pub use logic::*;
51
52mod mapping;
53pub use mapping::*;
54
55mod traits;
56pub use traits::*;
57
58mod bytes;
59mod parse;
60mod serialize;
61mod to_checksum;
62
63use console::{
64    network::{
65        ConsensusVersion,
66        consensus_config_value,
67        prelude::{
68            Debug,
69            Deserialize,
70            Deserializer,
71            Display,
72            Err,
73            Error,
74            ErrorKind,
75            Formatter,
76            FromBytes,
77            FromBytesDeserializer,
78            FromStr,
79            IoResult,
80            Itertools,
81            Network,
82            Parser,
83            ParserResult,
84            Read,
85            Result,
86            Sanitizer,
87            Serialize,
88            Serializer,
89            ToBytes,
90            ToBytesSerializer,
91            TypeName,
92            Write,
93            anyhow,
94            bail,
95            de,
96            ensure,
97            error,
98            fmt,
99            make_error,
100            many0,
101            many1,
102            map,
103            map_res,
104            tag,
105            take,
106        },
107    },
108    program::{
109        EntryType,
110        FinalizeType,
111        Identifier,
112        Locator,
113        PlaintextType,
114        ProgramID,
115        RecordType,
116        RegisterType,
117        StructType,
118        ValueType,
119    },
120    types::U8,
121};
122use snarkvm_utilities::{cfg_find_map, cfg_iter};
123
124use indexmap::{IndexMap, IndexSet};
125use std::collections::BTreeSet;
126use tiny_keccak::{Hasher, Sha3 as TinySha3};
127
128#[derive(Copy, Clone, PartialEq, Eq, Hash)]
129enum ProgramLabel<N: Network> {
130    /// A program constructor.
131    Constructor,
132    /// A named component.
133    Identifier(Identifier<N>),
134}
135
136#[cfg(not(feature = "serial"))]
137use rayon::prelude::*;
138
139#[derive(Copy, Clone, PartialEq, Eq, Hash)]
140enum ProgramDefinition {
141    /// A program constructor.
142    Constructor,
143    /// A program mapping.
144    Mapping,
145    /// A program struct.
146    Struct,
147    /// A program record.
148    Record,
149    /// A program closure.
150    Closure,
151    /// A program function.
152    Function,
153    /// A program view function.
154    View,
155}
156
157#[derive(Clone)]
158pub struct ProgramCore<N: Network> {
159    /// The ID of the program.
160    id: ProgramID<N>,
161    /// A map of the declared imports for the program.
162    imports: IndexMap<ProgramID<N>, Import<N>>,
163    /// A map of program labels to their program definitions.
164    components: IndexMap<ProgramLabel<N>, ProgramDefinition>,
165    /// An optional constructor for the program.
166    constructor: Option<ConstructorCore<N>>,
167    /// A map of the declared mappings for the program.
168    mappings: IndexMap<Identifier<N>, Mapping<N>>,
169    /// A map of the declared structs for the program.
170    structs: IndexMap<Identifier<N>, StructType<N>>,
171    /// A map of the declared record types for the program.
172    records: IndexMap<Identifier<N>, RecordType<N>>,
173    /// A map of the declared closures for the program.
174    closures: IndexMap<Identifier<N>, ClosureCore<N>>,
175    /// A map of the declared functions for the program.
176    functions: IndexMap<Identifier<N>, FunctionCore<N>>,
177    /// A map of the declared view functions for the program.
178    views: IndexMap<Identifier<N>, ViewCore<N>>,
179}
180
181impl<N: Network> PartialEq for ProgramCore<N> {
182    /// Compares two programs for equality, verifying that the components are in the same order.
183    /// The order of the components must match to ensure that deployment tree is well-formed.
184    fn eq(&self, other: &Self) -> bool {
185        // Check that the number of components is the same.
186        if self.components.len() != other.components.len() {
187            return false;
188        }
189        // Check that the components match in order.
190        for (left, right) in self.components.iter().zip_eq(other.components.iter()) {
191            if left != right {
192                return false;
193            }
194        }
195        // Check that the remaining fields match.
196        self.id == other.id
197            && self.imports == other.imports
198            && self.mappings == other.mappings
199            && self.structs == other.structs
200            && self.records == other.records
201            && self.closures == other.closures
202            && self.functions == other.functions
203            && self.views == other.views
204    }
205}
206
207impl<N: Network> Eq for ProgramCore<N> {}
208
209impl<N: Network> ProgramCore<N> {
210    /// A list of reserved keywords for Aleo programs, enforced at the parser level.
211    // New keywords should be enforced through `RESTRICTED_KEYWORDS` instead, if possible.
212    // Adding keywords to this list will require a backwards-compatible versioning for programs.
213    #[rustfmt::skip]
214    pub const KEYWORDS: &'static [&'static str] = &[
215        // Mode
216        "const",
217        "constant",
218        "public",
219        "private",
220        // Literals
221        "address",
222        "boolean",
223        "field",
224        "group",
225        "i8",
226        "i16",
227        "i32",
228        "i64",
229        "i128",
230        "u8",
231        "u16",
232        "u32",
233        "u64",
234        "u128",
235        "scalar",
236        "signature",
237        "string",
238        // Boolean
239        "true",
240        "false",
241        // Statements
242        "input",
243        "output",
244        "as",
245        "into",
246        // Record
247        "record",
248        "owner",
249        // Program
250        "transition",
251        "import",
252        "function",
253        "struct",
254        "closure",
255        "program",
256        "aleo",
257        "self",
258        "storage",
259        "mapping",
260        "key",
261        "value",
262        "async",
263        "finalize",
264        // Reserved (catch all)
265        "global",
266        "block",
267        "return",
268        "break",
269        "assert",
270        "continue",
271        "let",
272        "if",
273        "else",
274        "while",
275        "for",
276        "switch",
277        "case",
278        "default",
279        "match",
280        "enum",
281        "struct",
282        "union",
283        "trait",
284        "impl",
285        "type",
286        "future",
287    ];
288    /// A list of restricted keywords for Aleo programs, enforced at the VM-level for program hygiene.
289    /// Each entry is a tuple of the consensus version and a list of keywords.
290    /// If the current consensus version is greater than or equal to the specified version,
291    /// the keywords in the list should be restricted.
292    #[rustfmt::skip]
293    pub const RESTRICTED_KEYWORDS: &'static [(ConsensusVersion, &'static [&'static str])] = &[
294        (ConsensusVersion::V6, &["constructor"]),
295        (ConsensusVersion::V14, &["dynamic", "identifier"]),
296        (ConsensusVersion::V15, &["view"]),
297    ];
298
299    /// Initializes an empty program.
300    #[inline]
301    pub fn new(id: ProgramID<N>) -> Result<Self> {
302        // Ensure the program name is valid.
303        ensure!(!Self::is_reserved_keyword(id.name()), "Program name is invalid: {}", id.name());
304
305        Ok(Self {
306            id,
307            imports: IndexMap::new(),
308            constructor: None,
309            components: IndexMap::new(),
310            mappings: IndexMap::new(),
311            structs: IndexMap::new(),
312            records: IndexMap::new(),
313            closures: IndexMap::new(),
314            functions: IndexMap::new(),
315            views: IndexMap::new(),
316        })
317    }
318
319    /// Initializes the credits program.
320    #[inline]
321    pub fn credits() -> Result<Self> {
322        Self::from_str(include_str!("./resources/credits.aleo"))
323    }
324
325    /// Returns the ID of the program.
326    pub const fn id(&self) -> &ProgramID<N> {
327        &self.id
328    }
329
330    /// Returns the imports in the program.
331    pub const fn imports(&self) -> &IndexMap<ProgramID<N>, Import<N>> {
332        &self.imports
333    }
334
335    /// Returns the constructor for the program.
336    pub const fn constructor(&self) -> Option<&ConstructorCore<N>> {
337        self.constructor.as_ref()
338    }
339
340    /// Returns the mappings in the program.
341    pub const fn mappings(&self) -> &IndexMap<Identifier<N>, Mapping<N>> {
342        &self.mappings
343    }
344
345    /// Returns the structs in the program.
346    pub const fn structs(&self) -> &IndexMap<Identifier<N>, StructType<N>> {
347        &self.structs
348    }
349
350    /// Returns the records in the program.
351    pub const fn records(&self) -> &IndexMap<Identifier<N>, RecordType<N>> {
352        &self.records
353    }
354
355    /// Returns the closures in the program.
356    pub const fn closures(&self) -> &IndexMap<Identifier<N>, ClosureCore<N>> {
357        &self.closures
358    }
359
360    /// Returns the functions in the program.
361    pub const fn functions(&self) -> &IndexMap<Identifier<N>, FunctionCore<N>> {
362        &self.functions
363    }
364
365    /// Returns the view functions in the program.
366    pub const fn views(&self) -> &IndexMap<Identifier<N>, ViewCore<N>> {
367        &self.views
368    }
369
370    /// Returns `true` if the program contains an import with the given program ID.
371    pub fn contains_import(&self, id: &ProgramID<N>) -> bool {
372        self.imports.contains_key(id)
373    }
374
375    /// Returns `true` if the program contains a constructor.
376    pub const fn contains_constructor(&self) -> bool {
377        self.constructor.is_some()
378    }
379
380    /// Returns `true` if the program contains a mapping with the given name.
381    pub fn contains_mapping(&self, name: &Identifier<N>) -> bool {
382        self.mappings.contains_key(name)
383    }
384
385    /// Returns `true` if the program contains a struct with the given name.
386    pub fn contains_struct(&self, name: &Identifier<N>) -> bool {
387        self.structs.contains_key(name)
388    }
389
390    /// Returns `true` if the program contains a record with the given name.
391    pub fn contains_record(&self, name: &Identifier<N>) -> bool {
392        self.records.contains_key(name)
393    }
394
395    /// Returns `true` if the program contains a closure with the given name.
396    pub fn contains_closure(&self, name: &Identifier<N>) -> bool {
397        self.closures.contains_key(name)
398    }
399
400    /// Returns `true` if the program contains a function with the given name.
401    pub fn contains_function(&self, name: &Identifier<N>) -> bool {
402        self.functions.contains_key(name)
403    }
404
405    /// Returns `true` if the program contains a view function with the given name.
406    pub fn contains_view(&self, name: &Identifier<N>) -> bool {
407        self.views.contains_key(name)
408    }
409
410    /// Returns the mapping with the given name.
411    pub fn get_mapping(&self, name: &Identifier<N>) -> Result<Mapping<N>> {
412        // Attempt to retrieve the mapping.
413        let mapping = self.mappings.get(name).cloned().ok_or_else(|| anyhow!("Mapping '{name}' is not defined."))?;
414        // Ensure the mapping name matches.
415        ensure!(mapping.name() == name, "Expected mapping '{name}', but found mapping '{}'", mapping.name());
416        // Return the mapping.
417        Ok(mapping)
418    }
419
420    /// Returns the struct with the given name.
421    pub fn get_struct(&self, name: &Identifier<N>) -> Result<&StructType<N>> {
422        // Attempt to retrieve the struct.
423        let struct_ = self.structs.get(name).ok_or_else(|| anyhow!("Struct '{name}' is not defined."))?;
424        // Ensure the struct name matches.
425        ensure!(struct_.name() == name, "Expected struct '{name}', but found struct '{}'", struct_.name());
426        // Ensure the struct contains members.
427        ensure!(!struct_.members().is_empty(), "Struct '{name}' is missing members.");
428        // Return the struct.
429        Ok(struct_)
430    }
431
432    /// Returns the record with the given name.
433    pub fn get_record(&self, name: &Identifier<N>) -> Result<&RecordType<N>> {
434        // Attempt to retrieve the record.
435        let record = self.records.get(name).ok_or_else(|| anyhow!("Record '{name}' is not defined."))?;
436        // Ensure the record name matches.
437        ensure!(record.name() == name, "Expected record '{name}', but found record '{}'", record.name());
438        // Return the record.
439        Ok(record)
440    }
441
442    /// Returns the closure with the given name.
443    pub fn get_closure(&self, name: &Identifier<N>) -> Result<ClosureCore<N>> {
444        self.get_closure_ref(name).cloned()
445    }
446
447    /// Returns a reference to the closure with the given name.
448    pub fn get_closure_ref(&self, name: &Identifier<N>) -> Result<&ClosureCore<N>> {
449        // Attempt to retrieve the closure.
450        let closure = self.closures.get(name).ok_or_else(|| anyhow!("Closure '{name}' is not defined."))?;
451        // Ensure the closure name matches.
452        ensure!(closure.name() == name, "Expected closure '{name}', but found closure '{}'", closure.name());
453        // Ensure there are input statements in the closure.
454        ensure!(!closure.inputs().is_empty(), "Cannot evaluate a closure without input statements");
455        // Ensure the number of inputs is within the allowed range.
456        ensure!(closure.inputs().len() <= N::MAX_INPUTS, "Closure exceeds maximum number of inputs");
457        // Ensure there are instructions in the closure.
458        ensure!(!closure.instructions().is_empty(), "Cannot evaluate a closure without instructions");
459        // Ensure the number of outputs is within the allowed range.
460        ensure!(closure.outputs().len() <= N::MAX_OUTPUTS, "Closure exceeds maximum number of outputs");
461        // Return the closure.
462        Ok(closure)
463    }
464
465    /// Returns the function with the given name.
466    pub fn get_function(&self, name: &Identifier<N>) -> Result<FunctionCore<N>> {
467        self.get_function_ref(name).cloned()
468    }
469
470    /// Returns a reference to the function with the given name.
471    pub fn get_function_ref(&self, name: &Identifier<N>) -> Result<&FunctionCore<N>> {
472        // Attempt to retrieve the function.
473        let function = self.functions.get(name).ok_or(anyhow!("Function '{}/{name}' is not defined.", self.id))?;
474        // Ensure the function name matches.
475        ensure!(function.name() == name, "Expected function '{name}', but found function '{}'", function.name());
476        // Ensure the number of inputs is within the allowed range.
477        ensure!(function.inputs().len() <= N::MAX_INPUTS, "Function exceeds maximum number of inputs");
478        // Ensure the number of instructions is within the allowed range.
479        ensure!(function.instructions().len() <= N::MAX_INSTRUCTIONS, "Function exceeds maximum instructions");
480        // Ensure the number of outputs is within the allowed range.
481        ensure!(function.outputs().len() <= N::MAX_OUTPUTS, "Function exceeds maximum number of outputs");
482        // Return the function.
483        Ok(function)
484    }
485
486    /// Returns the view function with the given name.
487    pub fn get_view(&self, name: &Identifier<N>) -> Result<ViewCore<N>> {
488        self.get_view_ref(name).cloned()
489    }
490
491    /// Returns a reference to the view function with the given name.
492    pub fn get_view_ref(&self, name: &Identifier<N>) -> Result<&ViewCore<N>> {
493        let view = self.views.get(name).ok_or(anyhow!("View '{}/{name}' is not defined.", self.id))?;
494        ensure!(view.name() == name, "Expected view '{name}', but found view '{}'", view.name());
495        ensure!(view.inputs().len() <= N::MAX_INPUTS, "View exceeds maximum number of inputs");
496        ensure!(view.commands().len() <= N::MAX_COMMANDS, "View exceeds maximum number of commands");
497        ensure!(view.outputs().len() <= N::MAX_OUTPUTS, "View exceeds maximum number of outputs");
498        Ok(view)
499    }
500
501    /// Adds a new import statement to the program.
502    ///
503    /// # Errors
504    /// This method will halt if the imported program was previously added.
505    #[inline]
506    fn add_import(&mut self, import: Import<N>) -> Result<()> {
507        // Retrieve the imported program name.
508        let import_name = *import.name();
509
510        // Ensure that the number of imports is within the allowed range.
511        ensure!(self.imports.len() < N::MAX_IMPORTS, "Program exceeds the maximum number of imports");
512
513        // Ensure the import name is new.
514        ensure!(self.is_unique_name(&import_name), "'{import_name}' is already in use.");
515        // Ensure the import name is not a reserved opcode.
516        ensure!(!Self::is_reserved_opcode(&import_name.to_string()), "'{import_name}' is a reserved opcode.");
517        // Ensure the import name is not a reserved keyword.
518        ensure!(!Self::is_reserved_keyword(&import_name), "'{import_name}' is a reserved keyword.");
519
520        // Ensure the import is new.
521        ensure!(
522            !self.imports.contains_key(import.program_id()),
523            "Import '{}' is already defined.",
524            import.program_id()
525        );
526
527        // Add the import statement to the program.
528        if self.imports.insert(*import.program_id(), import.clone()).is_some() {
529            bail!("'{}' already exists in the program.", import.program_id())
530        }
531        Ok(())
532    }
533
534    /// Adds a constructor to the program.
535    ///
536    /// # Errors
537    /// This method will halt if a constructor was previously added.
538    /// This method will halt if a constructor exceeds the maximum number of commands.
539    fn add_constructor(&mut self, constructor: ConstructorCore<N>) -> Result<()> {
540        // Ensure the program does not already have a constructor.
541        ensure!(self.constructor.is_none(), "Program already has a constructor.");
542        // Ensure the number of commands is within the allowed range.
543        ensure!(!constructor.commands().is_empty(), "Constructor must have at least one command");
544        ensure!(constructor.commands().len() <= N::MAX_COMMANDS, "Constructor exceeds maximum number of commands");
545        // Add the constructor to the components.
546        if self.components.insert(ProgramLabel::Constructor, ProgramDefinition::Constructor).is_some() {
547            bail!("Constructor already exists in the program.")
548        }
549        // Add the constructor to the program.
550        self.constructor = Some(constructor);
551        Ok(())
552    }
553
554    /// Adds a new mapping to the program.
555    ///
556    /// # Errors
557    /// This method will halt if the mapping name is already in use.
558    /// This method will halt if the mapping name is a reserved opcode or keyword.
559    #[inline]
560    fn add_mapping(&mut self, mapping: Mapping<N>) -> Result<()> {
561        // Retrieve the mapping name.
562        let mapping_name = *mapping.name();
563
564        // Ensure the program has not exceeded the maximum number of mappings.
565        ensure!(self.mappings.len() < N::MAX_MAPPINGS, "Program exceeds the maximum number of mappings");
566
567        // Ensure the mapping name is new.
568        ensure!(self.is_unique_name(&mapping_name), "'{mapping_name}' is already in use.");
569        // Ensure the mapping name is not a reserved keyword.
570        ensure!(!Self::is_reserved_keyword(&mapping_name), "'{mapping_name}' is a reserved keyword.");
571        // Ensure the mapping name is not a reserved opcode.
572        ensure!(!Self::is_reserved_opcode(&mapping_name.to_string()), "'{mapping_name}' is a reserved opcode.");
573
574        // Add the mapping name to the identifiers.
575        if self.components.insert(ProgramLabel::Identifier(mapping_name), ProgramDefinition::Mapping).is_some() {
576            bail!("'{mapping_name}' already exists in the program.")
577        }
578        // Add the mapping to the program.
579        if self.mappings.insert(mapping_name, mapping).is_some() {
580            bail!("'{mapping_name}' already exists in the program.")
581        }
582        Ok(())
583    }
584
585    /// Adds a new struct to the program.
586    ///
587    /// # Errors
588    /// This method will halt if the struct was previously added.
589    /// This method will halt if the struct name is already in use in the program.
590    /// This method will halt if the struct name is a reserved opcode or keyword.
591    /// This method will halt if any structs in the struct's members are not already defined.
592    #[inline]
593    fn add_struct(&mut self, struct_: StructType<N>) -> Result<()> {
594        // Retrieve the struct name.
595        let struct_name = *struct_.name();
596
597        // Ensure the program has not exceeded the maximum number of structs.
598        ensure!(self.structs.len() < N::MAX_STRUCTS, "Program exceeds the maximum number of structs.");
599
600        // Ensure the struct name is new.
601        ensure!(self.is_unique_name(&struct_name), "'{struct_name}' is already in use.");
602        // Ensure the struct name is not a reserved opcode.
603        ensure!(!Self::is_reserved_opcode(&struct_name.to_string()), "'{struct_name}' is a reserved opcode.");
604        // Ensure the struct name is not a reserved keyword.
605        ensure!(!Self::is_reserved_keyword(&struct_name), "'{struct_name}' is a reserved keyword.");
606
607        // Ensure the struct contains members.
608        ensure!(!struct_.members().is_empty(), "Struct '{struct_name}' is missing members.");
609
610        // Ensure all struct members are well-formed.
611        // Note: This design ensures cyclic references are not possible.
612        for (identifier, plaintext_type) in struct_.members() {
613            // Ensure the member name is not a reserved keyword.
614            ensure!(!Self::is_reserved_keyword(identifier), "'{identifier}' is a reserved keyword.");
615            // Ensure the member type is already defined in the program.
616            match plaintext_type {
617                PlaintextType::Literal(_) => continue,
618                PlaintextType::Struct(member_identifier) => {
619                    // Ensure the member struct name exists in the program.
620                    if !self.structs.contains_key(member_identifier) {
621                        bail!("'{member_identifier}' in struct '{struct_name}' is not defined.")
622                    }
623                }
624                PlaintextType::ExternalStruct(locator) => {
625                    if !self.imports.contains_key(locator.program_id()) {
626                        bail!(
627                            "External program {} referenced in struct '{struct_name}' does not exist",
628                            locator.program_id()
629                        );
630                    }
631                }
632                PlaintextType::Array(array_type) => {
633                    match array_type.base_element_type() {
634                        PlaintextType::Struct(struct_name) =>
635                        // Ensure the member struct name exists in the program.
636                        {
637                            if !self.structs.contains_key(struct_name) {
638                                bail!("'{struct_name}' in array '{array_type}' is not defined.")
639                            }
640                        }
641                        PlaintextType::ExternalStruct(locator) => {
642                            if !self.imports.contains_key(locator.program_id()) {
643                                bail!(
644                                    "External program {} in array '{array_type}' does not exist",
645                                    locator.program_id()
646                                );
647                            }
648                        }
649                        PlaintextType::Array(..) | PlaintextType::Literal(..) => {}
650                    }
651                }
652            }
653        }
654
655        // Add the struct name to the identifiers.
656        if self.components.insert(ProgramLabel::Identifier(struct_name), ProgramDefinition::Struct).is_some() {
657            bail!("'{struct_name}' already exists in the program.")
658        }
659        // Add the struct to the program.
660        if self.structs.insert(struct_name, struct_).is_some() {
661            bail!("'{struct_name}' already exists in the program.")
662        }
663        Ok(())
664    }
665
666    /// Adds a new record to the program.
667    ///
668    /// # Errors
669    /// This method will halt if the record was previously added.
670    /// This method will halt if the record name is already in use in the program.
671    /// This method will halt if the record name is a reserved opcode or keyword.
672    /// This method will halt if any records in the record's members are not already defined.
673    #[inline]
674    fn add_record(&mut self, record: RecordType<N>) -> Result<()> {
675        // Retrieve the record name.
676        let record_name = *record.name();
677
678        // Ensure the program has not exceeded the maximum number of records.
679        ensure!(self.records.len() < N::MAX_RECORDS, "Program exceeds the maximum number of records.");
680
681        // Ensure the record name is new.
682        ensure!(self.is_unique_name(&record_name), "'{record_name}' is already in use.");
683        // Ensure the record name is not a reserved opcode.
684        ensure!(!Self::is_reserved_opcode(&record_name.to_string()), "'{record_name}' is a reserved opcode.");
685        // Ensure the record name is not a reserved keyword.
686        ensure!(!Self::is_reserved_keyword(&record_name), "'{record_name}' is a reserved keyword.");
687
688        // Ensure all record entries are well-formed.
689        // Note: This design ensures cyclic references are not possible.
690        for (identifier, entry_type) in record.entries() {
691            // Ensure the member name is not a reserved keyword.
692            ensure!(!Self::is_reserved_keyword(identifier), "'{identifier}' is a reserved keyword.");
693            // Ensure the member type is already defined in the program.
694            match entry_type.plaintext_type() {
695                PlaintextType::Literal(_) => continue,
696                PlaintextType::Struct(identifier) => {
697                    if !self.structs.contains_key(identifier) {
698                        bail!("Struct '{identifier}' in record '{record_name}' is not defined.")
699                    }
700                }
701                PlaintextType::ExternalStruct(locator) => {
702                    if !self.imports.contains_key(locator.program_id()) {
703                        bail!(
704                            "External program {} referenced in record '{record_name}' does not exist",
705                            locator.program_id()
706                        );
707                    }
708                }
709                PlaintextType::Array(array_type) => {
710                    match array_type.base_element_type() {
711                        PlaintextType::Struct(struct_name) =>
712                        // Ensure the member struct name exists in the program.
713                        {
714                            if !self.structs.contains_key(struct_name) {
715                                bail!("'{struct_name}' in array '{array_type}' is not defined.")
716                            }
717                        }
718                        PlaintextType::ExternalStruct(locator) => {
719                            if !self.imports.contains_key(locator.program_id()) {
720                                bail!(
721                                    "External program {} in array '{array_type}' does not exist",
722                                    locator.program_id()
723                                );
724                            }
725                        }
726                        PlaintextType::Array(..) | PlaintextType::Literal(..) => {}
727                    }
728                }
729            }
730        }
731
732        // Add the record name to the identifiers.
733        if self.components.insert(ProgramLabel::Identifier(record_name), ProgramDefinition::Record).is_some() {
734            bail!("'{record_name}' already exists in the program.")
735        }
736        // Add the record to the program.
737        if self.records.insert(record_name, record).is_some() {
738            bail!("'{record_name}' already exists in the program.")
739        }
740        Ok(())
741    }
742
743    /// Adds a new closure to the program.
744    ///
745    /// # Errors
746    /// This method will halt if the closure was previously added.
747    /// This method will halt if the closure name is already in use in the program.
748    /// This method will halt if the closure name is a reserved opcode or keyword.
749    /// This method will halt if any registers are assigned more than once.
750    /// This method will halt if the registers are not incrementing monotonically.
751    /// This method will halt if an input type references a non-existent definition.
752    /// This method will halt if an operand register does not already exist in memory.
753    /// This method will halt if a destination register already exists in memory.
754    /// This method will halt if an output register does not already exist.
755    /// This method will halt if an output type references a non-existent definition.
756    #[inline]
757    fn add_closure(&mut self, closure: ClosureCore<N>) -> Result<()> {
758        // Retrieve the closure name.
759        let closure_name = *closure.name();
760
761        // Ensure the program has not exceeded the maximum number of closures.
762        ensure!(self.closures.len() < N::MAX_CLOSURES, "Program exceeds the maximum number of closures.");
763
764        // Ensure the closure name is new.
765        ensure!(self.is_unique_name(&closure_name), "'{closure_name}' is already in use.");
766        // Ensure the closure name is not a reserved opcode.
767        ensure!(!Self::is_reserved_opcode(&closure_name.to_string()), "'{closure_name}' is a reserved opcode.");
768        // Ensure the closure name is not a reserved keyword.
769        ensure!(!Self::is_reserved_keyword(&closure_name), "'{closure_name}' is a reserved keyword.");
770
771        // Ensure there are input statements in the closure.
772        ensure!(!closure.inputs().is_empty(), "Cannot evaluate a closure without input statements");
773        // Ensure the number of inputs is within the allowed range.
774        ensure!(closure.inputs().len() <= N::MAX_INPUTS, "Closure exceeds maximum number of inputs");
775        // Ensure there are instructions in the closure.
776        ensure!(!closure.instructions().is_empty(), "Cannot evaluate a closure without instructions");
777        // Ensure the number of outputs is within the allowed range.
778        ensure!(closure.outputs().len() <= N::MAX_OUTPUTS, "Closure exceeds maximum number of outputs");
779
780        // Add the function name to the identifiers.
781        if self.components.insert(ProgramLabel::Identifier(closure_name), ProgramDefinition::Closure).is_some() {
782            bail!("'{closure_name}' already exists in the program.")
783        }
784        // Add the closure to the program.
785        if self.closures.insert(closure_name, closure).is_some() {
786            bail!("'{closure_name}' already exists in the program.")
787        }
788        Ok(())
789    }
790
791    /// Adds a new function to the program.
792    ///
793    /// # Errors
794    /// This method will halt if the function was previously added.
795    /// This method will halt if the function name is already in use in the program.
796    /// This method will halt if the function name is a reserved opcode or keyword.
797    /// This method will halt if any registers are assigned more than once.
798    /// This method will halt if the registers are not incrementing monotonically.
799    /// This method will halt if an input type references a non-existent definition.
800    /// This method will halt if an operand register does not already exist in memory.
801    /// This method will halt if a destination register already exists in memory.
802    /// This method will halt if an output register does not already exist.
803    /// This method will halt if an output type references a non-existent definition.
804    #[inline]
805    fn add_function(&mut self, function: FunctionCore<N>) -> Result<()> {
806        // Retrieve the function name.
807        let function_name = *function.name();
808
809        // Ensure the program has not exceeded the maximum number of functions.
810        ensure!(self.functions.len() < N::MAX_FUNCTIONS, "Program exceeds the maximum number of functions");
811
812        // Ensure the function name is new.
813        ensure!(self.is_unique_name(&function_name), "'{function_name}' is already in use.");
814        // Ensure the function name is not a reserved opcode.
815        ensure!(!Self::is_reserved_opcode(&function_name.to_string()), "'{function_name}' is a reserved opcode.");
816        // Ensure the function name is not a reserved keyword.
817        ensure!(!Self::is_reserved_keyword(&function_name), "'{function_name}' is a reserved keyword.");
818
819        // Ensure the number of inputs is within the allowed range.
820        ensure!(function.inputs().len() <= N::MAX_INPUTS, "Function exceeds maximum number of inputs");
821        // Ensure the number of instructions is within the allowed range.
822        ensure!(function.instructions().len() <= N::MAX_INSTRUCTIONS, "Function exceeds maximum instructions");
823        // Ensure the number of outputs is within the allowed range.
824        ensure!(function.outputs().len() <= N::MAX_OUTPUTS, "Function exceeds maximum number of outputs");
825
826        // Add the function name to the identifiers.
827        if self.components.insert(ProgramLabel::Identifier(function_name), ProgramDefinition::Function).is_some() {
828            bail!("'{function_name}' already exists in the program.")
829        }
830        // Add the function to the program.
831        if self.functions.insert(function_name, function).is_some() {
832            bail!("'{function_name}' already exists in the program.")
833        }
834        Ok(())
835    }
836
837    /// Adds a new view function to the program.
838    ///
839    /// # Errors
840    /// This method will halt if the view function name is already in use in the program.
841    /// This method will halt if the view function name is a reserved opcode or keyword.
842    #[inline]
843    fn add_view(&mut self, view: ViewCore<N>) -> Result<()> {
844        let view_name = *view.name();
845
846        ensure!(self.views.len() < N::MAX_VIEWS, "Program exceeds the maximum number of view functions.");
847
848        ensure!(self.is_unique_name(&view_name), "'{view_name}' is already in use.");
849        ensure!(!Self::is_reserved_opcode(&view_name.to_string()), "'{view_name}' is a reserved opcode.");
850        ensure!(!Self::is_reserved_keyword(&view_name), "'{view_name}' is a reserved keyword.");
851
852        // Views permit zero commands (passthrough / no-op) and zero outputs (guard views: the
853        // body asserts and the caller observes success via tx acceptance, failure via tx
854        // rejection; mirrors Solidity's zero-return `view` functions).
855        ensure!(view.inputs().len() <= N::MAX_INPUTS, "View exceeds maximum number of inputs");
856        ensure!(view.outputs().len() <= N::MAX_OUTPUTS, "View exceeds maximum number of outputs");
857
858        if self.components.insert(ProgramLabel::Identifier(view_name), ProgramDefinition::View).is_some() {
859            bail!("'{view_name}' already exists in the program.")
860        }
861        if self.views.insert(view_name, view).is_some() {
862            bail!("'{view_name}' already exists in the program.")
863        }
864        Ok(())
865    }
866
867    /// Returns `true` if the given name does not already exist in the program.
868    fn is_unique_name(&self, name: &Identifier<N>) -> bool {
869        !self.components.contains_key(&ProgramLabel::Identifier(*name))
870    }
871
872    /// Returns `true` if the given name is a reserved opcode.
873    pub fn is_reserved_opcode(name: &str) -> bool {
874        Instruction::<N>::is_reserved_opcode(name)
875    }
876
877    /// Returns `true` if the given name uses a reserved keyword.
878    pub fn is_reserved_keyword(name: &Identifier<N>) -> bool {
879        // Convert the given name to a string.
880        let name = name.to_string();
881        // Check if the name is a keyword.
882        Self::KEYWORDS.iter().any(|keyword| *keyword == name)
883    }
884
885    /// Returns an iterator over the restricted keywords for the given consensus version.
886    pub fn restricted_keywords_for_consensus_version(
887        consensus_version: ConsensusVersion,
888    ) -> impl Iterator<Item = &'static str> {
889        Self::RESTRICTED_KEYWORDS
890            .iter()
891            .filter(move |(version, _)| *version <= consensus_version)
892            .flat_map(|(_, keywords)| *keywords)
893            .copied()
894    }
895
896    /// Checks a program for restricted keywords for the given consensus version.
897    /// Returns an error if any restricted keywords are found.
898    /// Note: Restrictions are not enforced on the import names in case they were deployed before the restrictions were added.
899    pub fn check_restricted_keywords_for_consensus_version(&self, consensus_version: ConsensusVersion) -> Result<()> {
900        // Get all keywords that are restricted for the consensus version.
901        let keywords =
902            Program::<N>::restricted_keywords_for_consensus_version(consensus_version).collect::<IndexSet<_>>();
903        // Check if the program name is a restricted keywords.
904        let program_name = self.id().name().to_string();
905        if keywords.contains(&program_name.as_str()) {
906            bail!("Program name '{program_name}' is a restricted keyword for the current consensus version")
907        }
908        // Check that all top-level program components are not restricted keywords.
909        for component in self.components.keys() {
910            match component {
911                ProgramLabel::Identifier(identifier) => {
912                    if keywords.contains(identifier.to_string().as_str()) {
913                        bail!(
914                            "Program component '{identifier}' is a restricted keyword for the current consensus version"
915                        )
916                    }
917                }
918                ProgramLabel::Constructor => continue,
919            }
920        }
921        // Check that all record entry names are not restricted keywords.
922        for record_type in self.records().values() {
923            for entry_name in record_type.entries().keys() {
924                if keywords.contains(entry_name.to_string().as_str()) {
925                    bail!("Record entry '{entry_name}' is a restricted keyword for the current consensus version")
926                }
927            }
928        }
929        // Check that all struct member names are not restricted keywords.
930        for struct_type in self.structs().values() {
931            for member_name in struct_type.members().keys() {
932                if keywords.contains(member_name.to_string().as_str()) {
933                    bail!("Struct member '{member_name}' is a restricted keyword for the current consensus version")
934                }
935            }
936        }
937        // Check that all `finalize` positions.
938        // Note: It is sufficient to only check the positions in `FinalizeCore` since `FinalizeTypes::initialize` checks that every
939        // `Branch` instruction targets a valid position.
940        for function in self.functions().values() {
941            if let Some(finalize_logic) = function.finalize_logic() {
942                for position in finalize_logic.positions().keys() {
943                    if keywords.contains(position.to_string().as_str()) {
944                        bail!(
945                            "Finalize position '{position}' is a restricted keyword for the current consensus version"
946                        )
947                    }
948                }
949            }
950        }
951        Ok(())
952    }
953
954    /// Checks that the program structure is well-formed under the following rules:
955    ///  1. The program ID must not contain the keyword "aleo" in the program name.
956    ///  2. The record name must not contain the keyword "aleo".
957    ///  3. Record names must not be prefixes of other record names.
958    ///  4. Record entry names must not contain the keyword "aleo".
959    pub fn check_program_naming_structure(&self) -> Result<()> {
960        // 1. Check if the program ID contains the "aleo" substring
961        let program_id = self.id().name().to_string();
962        if program_id.contains("aleo") {
963            bail!("Program ID '{program_id}' can't contain the reserved keyword 'aleo'.");
964        }
965
966        // Fetch the record names in a sorted BTreeSet.
967        let record_names: BTreeSet<String> = self.records.keys().map(|name| name.to_string()).collect();
968
969        // 2. Check if any record name contains the "aleo" substring.
970        for record_name in &record_names {
971            if record_name.contains("aleo") {
972                bail!("Record name '{record_name}' can't contain the reserved keyword 'aleo'.");
973            }
974        }
975
976        // 3. Check if any of the record names are a prefix of another.
977        let mut record_names_iter = record_names.iter();
978        let mut previous_record_name = record_names_iter.next();
979        for record_name in record_names_iter {
980            if let Some(previous) = previous_record_name {
981                if record_name.starts_with(previous) {
982                    bail!("Record name '{previous}' can't be a prefix of record name '{record_name}'.");
983                }
984            }
985            previous_record_name = Some(record_name);
986        }
987
988        // 4. Check if any record entry names contain the "aleo" substring.
989        for record_entry_name in self.records.values().flat_map(|record_type| record_type.entries().keys()) {
990            if record_entry_name.to_string().contains("aleo") {
991                bail!("Record entry name '{record_entry_name}' can't contain the reserved keyword 'aleo'.");
992            }
993        }
994
995        Ok(())
996    }
997
998    /// Checks that the program does not make external calls to `credits.aleo/upgrade`.
999    pub fn check_external_calls_to_credits_upgrade(&self) -> Result<()> {
1000        // Check if the program makes external calls to `credits.aleo/upgrade`.
1001        cfg_iter!(self.functions()).flat_map(|(_, function)| function.instructions()).try_for_each(|instruction| {
1002            if let Some(CallOperator::Locator(locator)) = instruction.call_operator() {
1003                // Check if the locator is restricted.
1004                if locator.to_string() == "credits.aleo/upgrade" {
1005                    bail!("External call to restricted locator '{locator}'")
1006                }
1007            }
1008            Ok(())
1009        })?;
1010        Ok(())
1011    }
1012
1013    /// Returns `true` if a program contains any V9 syntax.
1014    /// This includes `constructor`, `Operand::Edition`, `Operand::Checksum`, and `Operand::ProgramOwner`.
1015    /// This is enforced to be `false` for programs before `ConsensusVersion::V9`.
1016    #[inline]
1017    pub fn contains_v9_syntax(&self) -> bool {
1018        // Check if the program contains a constructor.
1019        if self.contains_constructor() {
1020            return true;
1021        }
1022        // Check each instruction and output in each function's finalize scope for the use of
1023        // `Operand::Checksum`, `Operand::Edition` or `Operand::ProgramOwner`.
1024        for function in self.functions().values() {
1025            // Check the finalize scope if it exists.
1026            if let Some(finalize_logic) = function.finalize_logic() {
1027                // Check the command operands.
1028                for command in finalize_logic.commands() {
1029                    for operand in command.operands() {
1030                        if matches!(operand, Operand::Checksum(_) | Operand::Edition(_) | Operand::ProgramOwner(_)) {
1031                            return true;
1032                        }
1033                    }
1034                }
1035            }
1036        }
1037        // Return `false` since no V9 syntax was found.
1038        false
1039    }
1040
1041    /// Returns whether this program explicitly refers to an external struct, like `other_program.aleo/StructType`?
1042    ///
1043    /// This function exists to check if programs to be deployed use external structs so they can be gated
1044    /// by consensus version.
1045    pub fn contains_external_struct(&self) -> bool {
1046        self.mappings.values().any(|mapping| mapping.contains_external_struct())
1047            || self
1048                .structs
1049                .values()
1050                .flat_map(|struct_| struct_.members().values())
1051                .any(|plaintext_type| plaintext_type.contains_external_struct())
1052            || self
1053                .records
1054                .values()
1055                .flat_map(|record| record.entries().values())
1056                .any(|entry| entry.plaintext_type().contains_external_struct())
1057            || self.closures.values().any(|closure| closure.contains_external_struct())
1058            || self.functions.values().any(|function| function.contains_external_struct())
1059            || self.constructor.iter().any(|constructor| constructor.contains_external_struct())
1060            || self.views.values().any(|view| view.contains_external_struct())
1061    }
1062
1063    /// Returns `true` if this program violates pre-V13 rules for external records
1064    /// or futures by containing registers with non-local struct types across program boundaries.
1065    ///
1066    /// Notes:
1067    /// 1. We only need to check functions because closures and constructors cannot reference
1068    ///    external records or futures.
1069    /// 2. We need to check function inputs.
1070    /// 3. No need to check instructions other than `Call`. The only other instruction that can
1071    ///    refer to a record is a `cast` but we cannot cast to external records anyways.
1072    ///4.  No need to check function outputs, because they have already been checked in either the
1073    ///    inputs or call instruction.
1074    pub fn violates_pre_v13_external_record_and_future_rules<F0, F1, F2, F3>(
1075        &self,
1076        get_external_record: &F0,
1077        get_external_function: &F1,
1078        get_external_future: &F2,
1079        is_local_struct: &F3,
1080    ) -> bool
1081    where
1082        F0: Fn(&Locator<N>) -> Result<RecordType<N>>,
1083        F1: Fn(&Locator<N>) -> Result<FunctionCore<N>>,
1084        F2: Fn(&Locator<N>) -> Result<FinalizeCore<N>>,
1085        F3: Fn(&Identifier<N>) -> bool,
1086    {
1087        // Helper: does a plaintext type (possibly nested in arrays) reference a struct not defined locally?
1088        fn plaintext_uses_nonlocal_struct<N: Network>(
1089            ty: &PlaintextType<N>,
1090            is_local_struct: &impl Fn(&Identifier<N>) -> bool,
1091        ) -> bool {
1092            match ty {
1093                PlaintextType::Struct(name) => !is_local_struct(name),
1094                PlaintextType::Array(array_type) => {
1095                    plaintext_uses_nonlocal_struct(array_type.base_element_type(), is_local_struct)
1096                }
1097                _ => false,
1098            }
1099        }
1100
1101        // Helper: does a record contain any struct not defined locally?
1102        let record_uses_nonlocal_struct = |record: &RecordType<N>| {
1103            record.entries().iter().any(|(_, member)| match member {
1104                EntryType::Constant(ty) | EntryType::Private(ty) | EntryType::Public(ty) => {
1105                    plaintext_uses_nonlocal_struct(ty, is_local_struct)
1106                }
1107            })
1108        };
1109
1110        for function in self.functions.values() {
1111            // Scan function inputs for external record types.
1112            for input in function.inputs() {
1113                let ValueType::ExternalRecord(locator) = input.value_type() else {
1114                    continue;
1115                };
1116                let Ok(record) = get_external_record(locator) else {
1117                    continue;
1118                };
1119                if record_uses_nonlocal_struct(&record) {
1120                    return true;
1121                }
1122            }
1123
1124            // Scan instructions for calls to external programs.
1125            for instruction in function.instructions() {
1126                let Instruction::Call(call) = instruction else { continue };
1127                let CallOperator::Locator(locator) = call.operator() else { continue };
1128                let Ok(external_function) = get_external_function(locator) else {
1129                    continue;
1130                };
1131
1132                // Check if the outputs of the external function reference a struct that is not
1133                // locally available.
1134                for output in external_function.outputs() {
1135                    match output.value_type() {
1136                        ValueType::Record(identifier) => {
1137                            let locator = Locator::new(*locator.program_id(), *identifier);
1138                            let Ok(record) = get_external_record(&locator) else {
1139                                continue;
1140                            };
1141                            if record_uses_nonlocal_struct(&record) {
1142                                return true;
1143                            }
1144                        }
1145
1146                        ValueType::Future(loc) => {
1147                            let Ok(future) = get_external_future(loc) else {
1148                                continue;
1149                            };
1150                            for input in future.input_types() {
1151                                let FinalizeType::Plaintext(ty) = input else {
1152                                    continue;
1153                                };
1154
1155                                // We intentionally ignore `FinalizeType::Future(_)` here. Any such future
1156                                // originates from another program whose deployment would already have been
1157                                // validated under the same pre-V13 rules. At this point, only plaintext inputs
1158                                // can introduce new non-local struct violations.
1159
1160                                if plaintext_uses_nonlocal_struct(&ty, is_local_struct) {
1161                                    return true;
1162                                }
1163                            }
1164                        }
1165
1166                        _ => {}
1167                    }
1168                }
1169            }
1170        }
1171
1172        false
1173    }
1174
1175    /// Returns `true` if the program contains an array type with a size that exceeds the given maximum.
1176    pub fn exceeds_max_array_size(&self, max_array_size: u32) -> bool {
1177        self.mappings.values().any(|mapping| mapping.exceeds_max_array_size(max_array_size))
1178            || self.structs.values().any(|struct_type| struct_type.exceeds_max_array_size(max_array_size))
1179            || self.records.values().any(|record_type| record_type.exceeds_max_array_size(max_array_size))
1180            || self.closures.values().any(|closure| closure.exceeds_max_array_size(max_array_size))
1181            || self.functions.values().any(|function| function.exceeds_max_array_size(max_array_size))
1182            || self.constructor.iter().any(|constructor| constructor.exceeds_max_array_size(max_array_size))
1183            || self.views.values().any(|view| view.exceeds_max_array_size(max_array_size))
1184    }
1185
1186    /// Returns `true` if a program contains any V11 syntax.
1187    /// This includes:
1188    /// 1. `.raw` hash or signature verification variants
1189    /// 2. `ecdsa.verify.*` opcodes
1190    #[inline]
1191    pub fn contains_v11_syntax(&self) -> bool {
1192        // Helper to check if any of the opcodes:
1193        // - start with `ecdsa.verify`, `serialize`, or `deserialize`
1194        // - end with `.raw` or `.native`
1195        let has_op = |opcode: &str| {
1196            opcode.starts_with("ecdsa.verify")
1197                || opcode.starts_with("serialize")
1198                || opcode.starts_with("deserialize")
1199                || opcode.ends_with(".raw")
1200                || opcode.ends_with(".native")
1201        };
1202
1203        // Determine if any function instructions contain the new syntax.
1204        let function_contains = cfg_iter!(self.functions())
1205            .flat_map(|(_, function)| function.instructions())
1206            .any(|instruction| has_op(*instruction.opcode()));
1207
1208        // Determine if any closure instructions contain the new syntax.
1209        let closure_contains = cfg_iter!(self.closures())
1210            .flat_map(|(_, closure)| closure.instructions())
1211            .any(|instruction| has_op(*instruction.opcode()));
1212
1213        // Determine if any finalize commands or constructor commands contain the new syntax.
1214        let command_contains = cfg_iter!(self.functions())
1215            .flat_map(|(_, function)| function.finalize_logic().map(|finalize| finalize.commands()))
1216            .flatten()
1217            .chain(cfg_iter!(self.constructor).flat_map(|constructor| constructor.commands()))
1218            .any(|command| matches!(command, Command::Instruction(instruction) if has_op(*instruction.opcode())));
1219
1220        function_contains || closure_contains || command_contains
1221    }
1222
1223    /// Returns `true` if a program contains any V12 syntax.
1224    /// This includes `Operand::BlockTimestamp`.
1225    /// This is enforced to be `false` for programs before `ConsensusVersion::V12`.
1226    #[inline]
1227    pub fn contains_v12_syntax(&self) -> bool {
1228        // Check each instruction and output in each function's finalize scope for the use of
1229        // `Operand::BlockTimestamp`.
1230        cfg_iter!(self.functions()).any(|(_, function)| {
1231            function.finalize_logic().is_some_and(|finalize_logic| {
1232                cfg_iter!(finalize_logic.commands()).any(|command| {
1233                    cfg_iter!(command.operands()).any(|operand| matches!(operand, Operand::BlockTimestamp))
1234                })
1235            })
1236        })
1237    }
1238
1239    /// Checks that the program size does not exceed the maximum allowed size for the given block height.
1240    pub fn check_program_size(&self, block_height: u32) -> Result<()> {
1241        // Calculate the program size.
1242        let program_size = self.to_string().len();
1243        // Determine the maximum allowed program size for the current consensus version.
1244        let maximum_allowed_program_size = consensus_config_value!(N, MAX_PROGRAM_SIZE, block_height)
1245            .ok_or(anyhow!("Failed to fetch maximum program size"))?;
1246
1247        ensure!(
1248            program_size <= maximum_allowed_program_size,
1249            "Program size of {program_size} bytes exceeds the maximum allowed size of {maximum_allowed_program_size} bytes for the current height {block_height} (consensus version {}).",
1250            N::CONSENSUS_VERSION(block_height)?
1251        );
1252
1253        Ok(())
1254    }
1255
1256    /// Checks that the program writes size does not exceed the maximum allowed size for the given block height.
1257    pub fn check_program_writes(&self, block_height: u32) -> Result<()> {
1258        // Determine the maximum allowed number of writes for the current consensus version.
1259        let max_num_writes = consensus_config_value!(N, MAX_WRITES, block_height)
1260            .ok_or(anyhow!("Failed to fetch maximum number of writes"))?;
1261
1262        // Check if the constructor exceeds the maximum number of writes.
1263        if self.constructor().is_some_and(|constructor| constructor.num_writes() > max_num_writes) {
1264            bail!(
1265                "Program constructor exceeds the maximum allowed writes ({max_num_writes}) for the current height {block_height} (consensus version {}).",
1266                N::CONSENSUS_VERSION(block_height)?
1267            );
1268        }
1269
1270        // Find the first function whose finalize logic exceeds the maximum writes.
1271        if let Some(name) = cfg_find_map!(self.functions(), |function| {
1272            function
1273                .finalize_logic()
1274                .is_some_and(|finalize| finalize.num_writes() > max_num_writes)
1275                .then(|| *function.name())
1276        }) {
1277            bail!(
1278                "Program function '{name}' exceeds the maximum allowed writes ({max_num_writes}) for the current height {block_height} (consensus version {}).",
1279                N::CONSENSUS_VERSION(block_height)?
1280            );
1281        }
1282
1283        Ok(())
1284    }
1285
1286    /// Returns `true` if a program contains any V14 syntax.
1287    /// This includes:
1288    /// 1. `snark.verify.*` opcodes.
1289    /// 2. `Operand::AleoGenerator` or `Operand::AleoGeneratorPowers` operands.
1290    /// 3. `Literal::Identifier` operands.
1291    /// 4. `call.dynamic` instructions.
1292    /// 5. `get.record.dynamic` instructions.
1293    /// 6. `cast` instructions targeting `dynamic.record`.
1294    /// 7. `dynamic.record` or `dynamic.future` in function input or output types.
1295    /// 8. `contains.dynamic`, `get.dynamic`, or `get.or_use.dynamic` commands in finalize blocks.
1296    /// 9. `dynamic.record` or `dynamic.future` in closure input or output types.
1297    /// 10. `dynamic.future` in finalize block input types.
1298    /// 11. Identifier types in any type declarations.
1299    ///
1300    /// This is enforced to be `false` for programs before `ConsensusVersion::V14`.
1301    #[inline]
1302    pub fn contains_v14_syntax(&self) -> Result<bool> {
1303        /// Returns `true` if the instruction uses V14-only opcodes or operands (infallible checks).
1304        fn is_v14_instruction<N: Network>(instr: &Instruction<N>) -> bool {
1305            matches!(instr, Instruction::CallDynamic(_) | Instruction::GetRecordDynamic(_))
1306                || matches!(instr, Instruction::Cast(cast) if *cast.cast_type() == CastType::DynamicRecord)
1307                || instr.opcode().starts_with("snark.verify")
1308                || cfg_iter!(instr.operands()).any(|operand| {
1309                    matches!(
1310                        operand,
1311                        Operand::AleoGenerator
1312                            | Operand::AleoGeneratorPowers(_)
1313                            | Operand::Literal(console::program::Literal::Identifier(..))
1314                    )
1315                })
1316        }
1317
1318        /// Returns `true` if the command uses V14-only syntax (infallible checks).
1319        fn is_v14_command<N: Network>(cmd: &Command<N>) -> bool {
1320            matches!(cmd, Command::ContainsDynamic(_) | Command::GetDynamic(_) | Command::GetOrUseDynamic(_))
1321                || matches!(cmd, Command::Instruction(instr) if is_v14_instruction(instr))
1322        }
1323
1324        // Helper to check if a value type is a V14-only dynamic type.
1325        let is_dynamic_value_type =
1326            |vt: &ValueType<N>| matches!(vt, ValueType::DynamicRecord | ValueType::DynamicFuture);
1327
1328        // Helper to check if a register type is a V14-only dynamic type.
1329        let is_dynamic_register_type =
1330            |rt: &RegisterType<N>| matches!(rt, RegisterType::DynamicRecord | RegisterType::DynamicFuture);
1331
1332        // Check functions: instructions, finalize commands, and type declarations.
1333        for (_, function) in self.functions() {
1334            if function.instructions().iter().any(is_v14_instruction)
1335                || function.inputs().iter().any(|input| is_dynamic_value_type(input.value_type()))
1336                || function.outputs().iter().any(|output| is_dynamic_value_type(output.value_type()))
1337            {
1338                return Ok(true);
1339            }
1340            if let Some(finalize) = function.finalize_logic() {
1341                if finalize.inputs().iter().any(|input| matches!(input.finalize_type(), FinalizeType::DynamicFuture))
1342                    || finalize.commands().iter().any(is_v14_command)
1343                {
1344                    return Ok(true);
1345                }
1346            }
1347            if function.contains_identifier_type()? {
1348                return Ok(true);
1349            }
1350        }
1351
1352        // Check closures: instructions and type declarations.
1353        for (_, closure) in self.closures() {
1354            if closure.instructions().iter().any(is_v14_instruction)
1355                || closure.inputs().iter().any(|input| is_dynamic_register_type(input.register_type()))
1356                || closure.outputs().iter().any(|output| is_dynamic_register_type(output.register_type()))
1357            {
1358                return Ok(true);
1359            }
1360            if closure.contains_identifier_type()? {
1361                return Ok(true);
1362            }
1363        }
1364
1365        // Check constructor commands and identifier types.
1366        if let Some(constructor) = &self.constructor {
1367            if constructor.commands().iter().any(is_v14_command) {
1368                return Ok(true);
1369            }
1370            if constructor.contains_identifier_type()? {
1371                return Ok(true);
1372            }
1373        }
1374
1375        // Check remaining type definitions: mappings, structs, records.
1376        for mapping in self.mappings.values() {
1377            if mapping.contains_identifier_type()? {
1378                return Ok(true);
1379            }
1380        }
1381        for struct_type in self.structs.values() {
1382            if struct_type.contains_identifier_type()? {
1383                return Ok(true);
1384            }
1385        }
1386        for record_type in self.records.values() {
1387            if record_type.contains_identifier_type()? {
1388                return Ok(true);
1389            }
1390        }
1391
1392        Ok(false)
1393    }
1394
1395    /// Returns `true` if a program contains any V15 syntax.
1396    /// This includes:
1397    /// 1. `commit.*.raw` opcodes (raw commit variants).
1398    /// 2. `view` blocks (new on-disk component variant 6).
1399    /// 3. `call` instructions inside `finalize` bodies (pre-V15 finalize forbade `call`).
1400    ///
1401    /// This is enforced to be `false` for programs before `ConsensusVersion::V15`.
1402    #[inline]
1403    pub fn contains_v15_syntax(&self) -> bool {
1404        // Helper to check if an opcode is a raw commit variant.
1405        let has_op = |opcode: &str| opcode.starts_with("commit.") && opcode.ends_with(".raw");
1406
1407        // Determine if any function instructions contain the new syntax.
1408        let function_contains = cfg_iter!(self.functions())
1409            .flat_map(|(_, function)| function.instructions())
1410            .any(|instruction| has_op(*instruction.opcode()));
1411
1412        // Determine if any closure instructions contain the new syntax.
1413        let closure_contains = cfg_iter!(self.closures())
1414            .flat_map(|(_, closure)| closure.instructions())
1415            .any(|instruction| has_op(*instruction.opcode()));
1416
1417        // Determine if any finalize commands or constructor commands contain the new syntax.
1418        let command_contains = cfg_iter!(self.functions())
1419            .flat_map(|(_, function)| function.finalize_logic().map(|finalize| finalize.commands()))
1420            .flatten()
1421            .chain(cfg_iter!(self.constructor).flat_map(|constructor| constructor.commands()))
1422            .any(|command| matches!(command, Command::Instruction(instruction) if has_op(*instruction.opcode())));
1423
1424        // Detect `call` instructions inside finalize bodies. Pre-V15 finalize forbade `call`
1425        // entirely, so any deployed program with one is necessarily V15+ syntax.
1426        //
1427        // Intentionally matches only `Instruction::Call` — `CallDynamic` in a finalize body is
1428        // rejected at parse time by `Finalize::add_command`, so it can never reach a deployed
1429        // program. If `call.dynamic` is ever permitted in finalize, this clause must be widened
1430        // (and `Finalize::add_command`, `cost::cost_per_command`, and the finalize
1431        // type-checker's `Opcode::Call` arm need re-review at the same time).
1432        let finalize_has_call = cfg_iter!(self.functions())
1433            .filter_map(|(_, function)| function.finalize_logic())
1434            .flat_map(|finalize| finalize.commands())
1435            .any(|command| matches!(command, Command::Instruction(Instruction::Call(_))));
1436
1437        function_contains || closure_contains || command_contains || finalize_has_call || !self.views.is_empty()
1438    }
1439
1440    /// Returns `true` if a program contains any string type.
1441    /// Before ConsensusVersion::V12, variable-length string sampling when using them as inputs caused deployment synthesis to be inconsistent and abort with probability 63/64.
1442    /// After ConsensusVersion::V12, string types are disallowed.
1443    #[inline]
1444    pub fn contains_string_type(&self) -> bool {
1445        self.mappings.values().any(|mapping| mapping.contains_string_type())
1446            || self.structs.values().any(|struct_type| struct_type.contains_string_type())
1447            || self.records.values().any(|record_type| record_type.contains_string_type())
1448            || self.closures.values().any(|closure| closure.contains_string_type())
1449            || self.functions.values().any(|function| function.contains_string_type())
1450            || self.constructor.iter().any(|constructor| constructor.contains_string_type())
1451            || self.views.values().any(|view| view.contains_string_type())
1452    }
1453}
1454
1455impl<N: Network> TypeName for ProgramCore<N> {
1456    /// Returns the type name as a string.
1457    #[inline]
1458    fn type_name() -> &'static str {
1459        "program"
1460    }
1461}
1462
1463#[cfg(test)]
1464mod tests {
1465    use super::*;
1466    use console::{
1467        network::MainnetV0,
1468        program::{Locator, ValueType},
1469    };
1470
1471    type CurrentNetwork = MainnetV0;
1472
1473    #[test]
1474    fn test_program_mapping() -> Result<()> {
1475        // Create a new mapping.
1476        let mapping = Mapping::<CurrentNetwork>::from_str(
1477            r"
1478mapping message:
1479    key as field.public;
1480    value as field.public;",
1481        )?;
1482
1483        // Initialize a new program.
1484        let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {mapping}"))?;
1485        // Ensure the mapping was added.
1486        assert!(program.contains_mapping(&Identifier::from_str("message")?));
1487        // Ensure the retrieved mapping matches.
1488        assert_eq!(mapping.to_string(), program.get_mapping(&Identifier::from_str("message")?)?.to_string());
1489
1490        Ok(())
1491    }
1492
1493    #[test]
1494    fn test_program_struct() -> Result<()> {
1495        // Create a new struct.
1496        let struct_ = StructType::<CurrentNetwork>::from_str(
1497            r"
1498struct message:
1499    first as field;
1500    second as field;",
1501        )?;
1502
1503        // Initialize a new program.
1504        let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {struct_}"))?;
1505        // Ensure the struct was added.
1506        assert!(program.contains_struct(&Identifier::from_str("message")?));
1507        // Ensure the retrieved struct matches.
1508        assert_eq!(&struct_, program.get_struct(&Identifier::from_str("message")?)?);
1509
1510        Ok(())
1511    }
1512
1513    #[test]
1514    fn test_program_record() -> Result<()> {
1515        // Create a new record.
1516        let record = RecordType::<CurrentNetwork>::from_str(
1517            r"
1518record foo:
1519    owner as address.private;
1520    first as field.private;
1521    second as field.public;",
1522        )?;
1523
1524        // Initialize a new program.
1525        let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {record}"))?;
1526        // Ensure the record was added.
1527        assert!(program.contains_record(&Identifier::from_str("foo")?));
1528        // Ensure the retrieved record matches.
1529        assert_eq!(&record, program.get_record(&Identifier::from_str("foo")?)?);
1530
1531        Ok(())
1532    }
1533
1534    #[test]
1535    fn test_program_function() -> Result<()> {
1536        // Create a new function.
1537        let function = Function::<CurrentNetwork>::from_str(
1538            r"
1539function compute:
1540    input r0 as field.public;
1541    input r1 as field.private;
1542    add r0 r1 into r2;
1543    output r2 as field.private;",
1544        )?;
1545
1546        // Initialize a new program.
1547        let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {function}"))?;
1548        // Ensure the function was added.
1549        assert!(program.contains_function(&Identifier::from_str("compute")?));
1550        // Ensure the retrieved function matches.
1551        assert_eq!(function, program.get_function(&Identifier::from_str("compute")?)?);
1552
1553        Ok(())
1554    }
1555
1556    #[test]
1557    fn test_program_import() -> Result<()> {
1558        // Initialize a new program.
1559        let program = Program::<CurrentNetwork>::from_str(
1560            r"
1561import eth.aleo;
1562import usdc.aleo;
1563
1564program swap.aleo;
1565
1566// The `swap` function transfers ownership of the record
1567// for token A to the record owner of token B, and vice-versa.
1568function swap:
1569    // Input the record for token A.
1570    input r0 as eth.aleo/eth.record;
1571    // Input the record for token B.
1572    input r1 as usdc.aleo/usdc.record;
1573
1574    // Send the record for token A to the owner of token B.
1575    call eth.aleo/transfer r0 r1.owner r0.amount into r2 r3;
1576
1577    // Send the record for token B to the owner of token A.
1578    call usdc.aleo/transfer r1 r0.owner r1.amount into r4 r5;
1579
1580    // Output the new record for token A.
1581    output r2 as eth.aleo/eth.record;
1582    // Output the new record for token B.
1583    output r4 as usdc.aleo/usdc.record;
1584    ",
1585        )
1586        .unwrap();
1587
1588        // Ensure the program imports exist.
1589        assert!(program.contains_import(&ProgramID::from_str("eth.aleo")?));
1590        assert!(program.contains_import(&ProgramID::from_str("usdc.aleo")?));
1591
1592        // Retrieve the 'swap' function.
1593        let function = program.get_function(&Identifier::from_str("swap")?)?;
1594
1595        // Ensure there are two inputs.
1596        assert_eq!(function.inputs().len(), 2);
1597        assert_eq!(function.input_types().len(), 2);
1598
1599        // Declare the expected input types.
1600        let expected_input_type_1 = ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?);
1601        let expected_input_type_2 = ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?);
1602
1603        // Ensure the inputs are external records.
1604        assert_eq!(function.input_types()[0], expected_input_type_1);
1605        assert_eq!(function.input_types()[1], expected_input_type_2);
1606
1607        // Ensure the input variants are correct.
1608        assert_eq!(function.input_types()[0].variant(), expected_input_type_1.variant());
1609        assert_eq!(function.input_types()[1].variant(), expected_input_type_2.variant());
1610
1611        // Ensure there are two instructions.
1612        assert_eq!(function.instructions().len(), 2);
1613
1614        // Ensure the instructions are calls.
1615        assert_eq!(function.instructions()[0].opcode(), Opcode::Call("call"));
1616        assert_eq!(function.instructions()[1].opcode(), Opcode::Call("call"));
1617
1618        // Ensure there are two outputs.
1619        assert_eq!(function.outputs().len(), 2);
1620        assert_eq!(function.output_types().len(), 2);
1621
1622        // Declare the expected output types.
1623        let expected_output_type_1 = ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?);
1624        let expected_output_type_2 = ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?);
1625
1626        // Ensure the outputs are external records.
1627        assert_eq!(function.output_types()[0], expected_output_type_1);
1628        assert_eq!(function.output_types()[1], expected_output_type_2);
1629
1630        // Ensure the output variants are correct.
1631        assert_eq!(function.output_types()[0].variant(), expected_output_type_1.variant());
1632        assert_eq!(function.output_types()[1].variant(), expected_output_type_2.variant());
1633
1634        Ok(())
1635    }
1636
1637    #[test]
1638    fn test_program_with_constructor() {
1639        // Initialize a new program.
1640        let program_string = r"import credits.aleo;
1641
1642program good_constructor.aleo;
1643
1644constructor:
1645    assert.eq edition 0u16;
1646    assert.eq credits.aleo/edition 0u16;
1647    assert.neq checksum 0field;
1648    assert.eq credits.aleo/checksum 6192738754253668739186185034243585975029374333074931926190215457304721124008field;
1649    set 1u8 into data[0u8];
1650
1651mapping data:
1652    key as u8.public;
1653    value as u8.public;
1654
1655function dummy:
1656
1657function check:
1658    async check into r0;
1659    output r0 as good_constructor.aleo/check.future;
1660
1661finalize check:
1662    get data[0u8] into r0;
1663    assert.eq r0 1u8;
1664";
1665        let program = Program::<CurrentNetwork>::from_str(program_string).unwrap();
1666
1667        // Check that the string and bytes (de)serialization works.
1668        let serialized = program.to_string();
1669        let deserialized = Program::<CurrentNetwork>::from_str(&serialized).unwrap();
1670        assert_eq!(program, deserialized);
1671
1672        let serialized = program.to_bytes_le().unwrap();
1673        let deserialized = Program::<CurrentNetwork>::from_bytes_le(&serialized).unwrap();
1674        assert_eq!(program, deserialized);
1675
1676        // Check that the display works.
1677        let display = format!("{program}");
1678        assert_eq!(display, program_string);
1679
1680        // Ensure the program contains a constructor.
1681        assert!(program.contains_constructor());
1682        assert_eq!(program.constructor().unwrap().commands().len(), 5);
1683    }
1684
1685    #[test]
1686    fn test_program_equality_and_checksum() {
1687        fn run_test(program1: &str, program2: &str, expected_equal: bool) {
1688            println!("Comparing programs:\n{program1}\n{program2}");
1689            let program1 = Program::<CurrentNetwork>::from_str(program1).unwrap();
1690            let program2 = Program::<CurrentNetwork>::from_str(program2).unwrap();
1691            assert_eq!(program1 == program2, expected_equal);
1692            assert_eq!(program1.to_checksum() == program2.to_checksum(), expected_equal);
1693        }
1694
1695        // Test two identical programs, with different whitespace.
1696        run_test(r"program test.aleo; function dummy:    ", r"program  test.aleo;     function dummy:   ", true);
1697
1698        // Test two programs, one with a different function name.
1699        run_test(r"program test.aleo; function dummy:    ", r"program test.aleo; function bummy:   ", false);
1700
1701        // Test two programs, one with a constructor and one without.
1702        run_test(
1703            r"program test.aleo; function dummy:    ",
1704            r"program test.aleo; constructor: assert.eq true true; function dummy: ",
1705            false,
1706        );
1707
1708        // Test two programs, both with a struct and function, but in different order.
1709        run_test(
1710            r"program test.aleo; struct foo: data as u8; function dummy:",
1711            r"program test.aleo; function dummy: struct foo: data as u8;",
1712            false,
1713        );
1714    }
1715
1716    #[test]
1717    fn test_contains_v14_syntax() -> Result<()> {
1718        // A baseline program with no V14 syntax.
1719        let no_v14 = Program::<CurrentNetwork>::from_str(
1720            r"program test.aleo;
1721function foo:
1722    input r0 as u64.public;
1723    output r0 as u64.public;",
1724        )?;
1725        assert!(!no_v14.contains_v14_syntax()?);
1726
1727        // A program with `dynamic.record` as a function input type.
1728        let dynamic_record_input = Program::<CurrentNetwork>::from_str(
1729            r"program test.aleo;
1730function foo:
1731    input r0 as dynamic.record;",
1732        )?;
1733        assert!(dynamic_record_input.contains_v14_syntax()?);
1734
1735        // A program with `dynamic.future` as a function output type.
1736        let dynamic_future_output = Program::<CurrentNetwork>::from_str(
1737            r"program test.aleo;
1738function foo:
1739    output r0 as dynamic.future;",
1740        )?;
1741        assert!(dynamic_future_output.contains_v14_syntax()?);
1742
1743        // A program with a `call.dynamic` instruction.
1744        let call_dynamic = Program::<CurrentNetwork>::from_str(
1745            r"program test.aleo;
1746function foo:
1747    input r0 as field.public;
1748    input r1 as field.public;
1749    input r2 as field.public;
1750    call.dynamic r0 r1 r2 into r3 (as u64.public);
1751    output r3 as u64.public;",
1752        )?;
1753        assert!(call_dynamic.contains_v14_syntax()?);
1754
1755        // A program with a `get.record.dynamic` instruction.
1756        let get_record_dynamic = Program::<CurrentNetwork>::from_str(
1757            r"program test.aleo;
1758function foo:
1759    input r0 as dynamic.record;
1760    get.record.dynamic r0.amount into r1 as field;
1761    output r1 as field.public;",
1762        )?;
1763        assert!(get_record_dynamic.contains_v14_syntax()?);
1764
1765        // A program with a `cast ... as dynamic.record` instruction.
1766        let cast_dynamic_record = Program::<CurrentNetwork>::from_str(
1767            r"program test.aleo;
1768record token:
1769    owner as address.private;
1770    amount as u64.private;
1771function foo:
1772    input r0 as token.record;
1773    cast r0 into r1 as dynamic.record;
1774    output r0.owner as address.private;",
1775        )?;
1776        assert!(cast_dynamic_record.contains_v14_syntax()?);
1777
1778        // A program with `contains.dynamic` in a finalize block.
1779        let contains_dynamic = Program::<CurrentNetwork>::from_str(
1780            r"program test.aleo;
1781function bar:
1782    input r0 as field.public;
1783    input r1 as field.public;
1784    input r2 as field.public;
1785    input r3 as field.public;
1786    async bar r0 r1 r2 r3 into r4;
1787    output r4 as test.aleo/bar.future;
1788finalize bar:
1789    input r0 as field.public;
1790    input r1 as field.public;
1791    input r2 as field.public;
1792    input r3 as field.public;
1793    contains.dynamic r0 r1 r2[r3] into r4;",
1794        )?;
1795        assert!(contains_dynamic.contains_v14_syntax()?);
1796
1797        // A program with `get.dynamic` in a finalize block.
1798        let get_dynamic = Program::<CurrentNetwork>::from_str(
1799            r"program test.aleo;
1800function bar:
1801    input r0 as field.public;
1802    input r1 as field.public;
1803    input r2 as field.public;
1804    input r3 as field.public;
1805    async bar r0 r1 r2 r3 into r4;
1806    output r4 as test.aleo/bar.future;
1807finalize bar:
1808    input r0 as field.public;
1809    input r1 as field.public;
1810    input r2 as field.public;
1811    input r3 as field.public;
1812    get.dynamic r0 r1 r2[r3] into r4 as field;",
1813        )?;
1814        assert!(get_dynamic.contains_v14_syntax()?);
1815
1816        // A program with `dynamic.future` as a finalize input type.
1817        let dynamic_future_finalize_input = Program::<CurrentNetwork>::from_str(
1818            r"program test.aleo;
1819function foo:
1820    input r0 as field.public;
1821    async foo r0 into r1;
1822    output r1 as test.aleo/foo.future;
1823finalize foo:
1824    input r0 as dynamic.future;
1825    await r0;",
1826        )?;
1827        assert!(dynamic_future_finalize_input.contains_v14_syntax()?);
1828
1829        // A program with `dynamic.record` as a closure input type.
1830        let closure_dynamic_input = Program::<CurrentNetwork>::from_str(
1831            r"program test.aleo;
1832closure bar:
1833    input r0 as dynamic.record;
1834    input r1 as field;
1835    add r1 r1 into r2;",
1836        )?;
1837        assert!(closure_dynamic_input.contains_v14_syntax()?);
1838
1839        // A program with `dynamic.record` as a closure output type.
1840        let closure_dynamic_output = Program::<CurrentNetwork>::from_str(
1841            r"program test.aleo;
1842closure bar:
1843    input r0 as field;
1844    add r0 r0 into r1;
1845    output r1 as dynamic.record;",
1846        )?;
1847        assert!(closure_dynamic_output.contains_v14_syntax()?);
1848
1849        // A program with `get.or_use.dynamic` in a finalize block.
1850        let get_or_use_dynamic = Program::<CurrentNetwork>::from_str(
1851            r"program test.aleo;
1852function bar:
1853    input r0 as field.public;
1854    input r1 as field.public;
1855    input r2 as field.public;
1856    input r3 as field.public;
1857    input r4 as u64.public;
1858    async bar r0 r1 r2 r3 r4 into r5;
1859    output r5 as test.aleo/bar.future;
1860finalize bar:
1861    input r0 as field.public;
1862    input r1 as field.public;
1863    input r2 as field.public;
1864    input r3 as field.public;
1865    input r4 as u64.public;
1866    get.or_use.dynamic r0 r1 r2[r3] r4 into r5 as u64;",
1867        )?;
1868        assert!(get_or_use_dynamic.contains_v14_syntax()?);
1869
1870        // A program with `snark.verify` in a finalize block.
1871        let snark_verify = Program::<CurrentNetwork>::from_str(
1872            r"program test.aleo;
1873function foo:
1874    input r0 as [u8; 8u32].public;
1875    input r1 as [field; 1u32].public;
1876    input r2 as [u8; 8u32].public;
1877    async foo r0 r1 r2 into r3;
1878    output r3 as test.aleo/foo.future;
1879finalize foo:
1880    input r0 as [u8; 8u32].public;
1881    input r1 as [field; 1u32].public;
1882    input r2 as [u8; 8u32].public;
1883    snark.verify r0 1u8 r1 r2 into r3;",
1884        )?;
1885        assert!(snark_verify.contains_v14_syntax()?);
1886
1887        // A program with `snark.verify.batch` in a finalize block.
1888        let snark_verify_batch = Program::<CurrentNetwork>::from_str(
1889            r"program test.aleo;
1890function foo:
1891    input r0 as [[u8; 8u32]; 1u32].public;
1892    input r1 as [[[field; 1u32]; 1u32]; 1u32].public;
1893    input r2 as [u8; 8u32].public;
1894    async foo r0 r1 r2 into r3;
1895    output r3 as test.aleo/foo.future;
1896finalize foo:
1897    input r0 as [[u8; 8u32]; 1u32].public;
1898    input r1 as [[[field; 1u32]; 1u32]; 1u32].public;
1899    input r2 as [u8; 8u32].public;
1900    snark.verify.batch r0 1u8 r1 r2 into r3;",
1901        )?;
1902        assert!(snark_verify_batch.contains_v14_syntax()?);
1903
1904        // A program with `aleo::GENERATOR` as an operand.
1905        let aleo_generator = Program::<CurrentNetwork>::from_str(
1906            r"program test.aleo;
1907function foo:
1908    input r0 as scalar.public;
1909    mul aleo::GENERATOR r0 into r1;
1910    output r1 as group.public;",
1911        )?;
1912        assert!(aleo_generator.contains_v14_syntax()?);
1913
1914        // A program with `aleo::GENERATOR_POWERS` as an operand.
1915        let aleo_generator_powers = Program::<CurrentNetwork>::from_str(
1916            r"program test.aleo;
1917function foo:
1918    input r0 as scalar.public;
1919    mul aleo::GENERATOR_POWERS[0u32] r0 into r1;
1920    output r1 as group.public;",
1921        )?;
1922        assert!(aleo_generator_powers.contains_v14_syntax()?);
1923
1924        // A program with `dynamic.future` as a closure output type.
1925        let closure_dynamic_future_output = Program::<CurrentNetwork>::from_str(
1926            r"program test.aleo;
1927closure bar:
1928    input r0 as field;
1929    add r0 r0 into r1;
1930    output r1 as dynamic.future;",
1931        )?;
1932        assert!(closure_dynamic_future_output.contains_v14_syntax()?);
1933
1934        // A program with a V14 opcode (`aleo::GENERATOR`) in a constructor block.
1935        let constructor_v14 = Program::<CurrentNetwork>::from_str(
1936            r"program test.aleo;
1937function dummy:
1938    input r0 as field.public;
1939    output r0 as field.public;
1940constructor:
1941    assert.eq aleo::GENERATOR aleo::GENERATOR;",
1942        )?;
1943        assert!(constructor_v14.contains_v14_syntax()?);
1944
1945        Ok(())
1946    }
1947}