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 V16 syntax.
1441    /// This includes the `Operand::ComponentChecksum` operand.
1442    /// This is enforced to be `false` for programs before `ConsensusVersion::V16`.
1443    #[inline]
1444    pub fn contains_v16_syntax(&self) -> bool {
1445        // Check each command operand in every finalize, constructor, and view scope for `Operand::ComponentChecksum`.
1446        let command_contains = cfg_iter!(self.functions())
1447            .flat_map(|(_, function)| function.finalize_logic().map(|finalize| finalize.commands()))
1448            .flatten()
1449            .chain(cfg_iter!(self.constructor).flat_map(|constructor| constructor.commands()))
1450            .chain(cfg_iter!(self.views).flat_map(|(_, view)| view.commands()))
1451            .flat_map(|command| command.operands())
1452            .any(|operand| matches!(operand, Operand::ComponentChecksum(..)));
1453
1454        // Views additionally have output operands, which are not commands and so are checked separately.
1455        let view_output_contains = self
1456            .views
1457            .values()
1458            .any(|view| view.outputs().iter().any(|output| matches!(output.operand(), Operand::ComponentChecksum(..))));
1459
1460        command_contains || view_output_contains
1461    }
1462
1463    /// Returns `true` if a program contains any string type.
1464    /// Before ConsensusVersion::V12, variable-length string sampling when using them as inputs caused deployment synthesis to be inconsistent and abort with probability 63/64.
1465    /// After ConsensusVersion::V12, string types are disallowed.
1466    #[inline]
1467    pub fn contains_string_type(&self) -> bool {
1468        self.mappings.values().any(|mapping| mapping.contains_string_type())
1469            || self.structs.values().any(|struct_type| struct_type.contains_string_type())
1470            || self.records.values().any(|record_type| record_type.contains_string_type())
1471            || self.closures.values().any(|closure| closure.contains_string_type())
1472            || self.functions.values().any(|function| function.contains_string_type())
1473            || self.constructor.iter().any(|constructor| constructor.contains_string_type())
1474            || self.views.values().any(|view| view.contains_string_type())
1475    }
1476}
1477
1478impl<N: Network> TypeName for ProgramCore<N> {
1479    /// Returns the type name as a string.
1480    #[inline]
1481    fn type_name() -> &'static str {
1482        "program"
1483    }
1484}
1485
1486#[cfg(test)]
1487mod tests {
1488    use super::*;
1489    use console::{
1490        network::MainnetV0,
1491        program::{Locator, ValueType},
1492    };
1493
1494    type CurrentNetwork = MainnetV0;
1495
1496    #[test]
1497    fn test_program_mapping() -> Result<()> {
1498        // Create a new mapping.
1499        let mapping = Mapping::<CurrentNetwork>::from_str(
1500            r"
1501mapping message:
1502    key as field.public;
1503    value as field.public;",
1504        )?;
1505
1506        // Initialize a new program.
1507        let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {mapping}"))?;
1508        // Ensure the mapping was added.
1509        assert!(program.contains_mapping(&Identifier::from_str("message")?));
1510        // Ensure the retrieved mapping matches.
1511        assert_eq!(mapping.to_string(), program.get_mapping(&Identifier::from_str("message")?)?.to_string());
1512
1513        Ok(())
1514    }
1515
1516    #[test]
1517    fn test_program_struct() -> Result<()> {
1518        // Create a new struct.
1519        let struct_ = StructType::<CurrentNetwork>::from_str(
1520            r"
1521struct message:
1522    first as field;
1523    second as field;",
1524        )?;
1525
1526        // Initialize a new program.
1527        let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {struct_}"))?;
1528        // Ensure the struct was added.
1529        assert!(program.contains_struct(&Identifier::from_str("message")?));
1530        // Ensure the retrieved struct matches.
1531        assert_eq!(&struct_, program.get_struct(&Identifier::from_str("message")?)?);
1532
1533        Ok(())
1534    }
1535
1536    #[test]
1537    fn test_program_record() -> Result<()> {
1538        // Create a new record.
1539        let record = RecordType::<CurrentNetwork>::from_str(
1540            r"
1541record foo:
1542    owner as address.private;
1543    first as field.private;
1544    second as field.public;",
1545        )?;
1546
1547        // Initialize a new program.
1548        let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {record}"))?;
1549        // Ensure the record was added.
1550        assert!(program.contains_record(&Identifier::from_str("foo")?));
1551        // Ensure the retrieved record matches.
1552        assert_eq!(&record, program.get_record(&Identifier::from_str("foo")?)?);
1553
1554        Ok(())
1555    }
1556
1557    #[test]
1558    fn test_program_function() -> Result<()> {
1559        // Create a new function.
1560        let function = Function::<CurrentNetwork>::from_str(
1561            r"
1562function compute:
1563    input r0 as field.public;
1564    input r1 as field.private;
1565    add r0 r1 into r2;
1566    output r2 as field.private;",
1567        )?;
1568
1569        // Initialize a new program.
1570        let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {function}"))?;
1571        // Ensure the function was added.
1572        assert!(program.contains_function(&Identifier::from_str("compute")?));
1573        // Ensure the retrieved function matches.
1574        assert_eq!(function, program.get_function(&Identifier::from_str("compute")?)?);
1575
1576        Ok(())
1577    }
1578
1579    #[test]
1580    fn test_program_import() -> Result<()> {
1581        // Initialize a new program.
1582        let program = Program::<CurrentNetwork>::from_str(
1583            r"
1584import eth.aleo;
1585import usdc.aleo;
1586
1587program swap.aleo;
1588
1589// The `swap` function transfers ownership of the record
1590// for token A to the record owner of token B, and vice-versa.
1591function swap:
1592    // Input the record for token A.
1593    input r0 as eth.aleo/eth.record;
1594    // Input the record for token B.
1595    input r1 as usdc.aleo/usdc.record;
1596
1597    // Send the record for token A to the owner of token B.
1598    call eth.aleo/transfer r0 r1.owner r0.amount into r2 r3;
1599
1600    // Send the record for token B to the owner of token A.
1601    call usdc.aleo/transfer r1 r0.owner r1.amount into r4 r5;
1602
1603    // Output the new record for token A.
1604    output r2 as eth.aleo/eth.record;
1605    // Output the new record for token B.
1606    output r4 as usdc.aleo/usdc.record;
1607    ",
1608        )
1609        .unwrap();
1610
1611        // Ensure the program imports exist.
1612        assert!(program.contains_import(&ProgramID::from_str("eth.aleo")?));
1613        assert!(program.contains_import(&ProgramID::from_str("usdc.aleo")?));
1614
1615        // Retrieve the 'swap' function.
1616        let function = program.get_function(&Identifier::from_str("swap")?)?;
1617
1618        // Ensure there are two inputs.
1619        assert_eq!(function.inputs().len(), 2);
1620        assert_eq!(function.input_types().len(), 2);
1621
1622        // Declare the expected input types.
1623        let expected_input_type_1 = ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?);
1624        let expected_input_type_2 = ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?);
1625
1626        // Ensure the inputs are external records.
1627        assert_eq!(function.input_types()[0], expected_input_type_1);
1628        assert_eq!(function.input_types()[1], expected_input_type_2);
1629
1630        // Ensure the input variants are correct.
1631        assert_eq!(function.input_types()[0].variant(), expected_input_type_1.variant());
1632        assert_eq!(function.input_types()[1].variant(), expected_input_type_2.variant());
1633
1634        // Ensure there are two instructions.
1635        assert_eq!(function.instructions().len(), 2);
1636
1637        // Ensure the instructions are calls.
1638        assert_eq!(function.instructions()[0].opcode(), Opcode::Call("call"));
1639        assert_eq!(function.instructions()[1].opcode(), Opcode::Call("call"));
1640
1641        // Ensure there are two outputs.
1642        assert_eq!(function.outputs().len(), 2);
1643        assert_eq!(function.output_types().len(), 2);
1644
1645        // Declare the expected output types.
1646        let expected_output_type_1 = ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?);
1647        let expected_output_type_2 = ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?);
1648
1649        // Ensure the outputs are external records.
1650        assert_eq!(function.output_types()[0], expected_output_type_1);
1651        assert_eq!(function.output_types()[1], expected_output_type_2);
1652
1653        // Ensure the output variants are correct.
1654        assert_eq!(function.output_types()[0].variant(), expected_output_type_1.variant());
1655        assert_eq!(function.output_types()[1].variant(), expected_output_type_2.variant());
1656
1657        Ok(())
1658    }
1659
1660    #[test]
1661    fn test_program_with_constructor() {
1662        // Initialize a new program.
1663        let program_string = r"import credits.aleo;
1664
1665program good_constructor.aleo;
1666
1667constructor:
1668    assert.eq edition 0u16;
1669    assert.eq credits.aleo/edition 0u16;
1670    assert.neq checksum 0field;
1671    assert.eq credits.aleo/checksum 6192738754253668739186185034243585975029374333074931926190215457304721124008field;
1672    set 1u8 into data[0u8];
1673
1674mapping data:
1675    key as u8.public;
1676    value as u8.public;
1677
1678function dummy:
1679
1680function check:
1681    async check into r0;
1682    output r0 as good_constructor.aleo/check.future;
1683
1684finalize check:
1685    get data[0u8] into r0;
1686    assert.eq r0 1u8;
1687";
1688        let program = Program::<CurrentNetwork>::from_str(program_string).unwrap();
1689
1690        // Check that the string and bytes (de)serialization works.
1691        let serialized = program.to_string();
1692        let deserialized = Program::<CurrentNetwork>::from_str(&serialized).unwrap();
1693        assert_eq!(program, deserialized);
1694
1695        let serialized = program.to_bytes_le().unwrap();
1696        let deserialized = Program::<CurrentNetwork>::from_bytes_le(&serialized).unwrap();
1697        assert_eq!(program, deserialized);
1698
1699        // Check that the display works.
1700        let display = format!("{program}");
1701        assert_eq!(display, program_string);
1702
1703        // Ensure the program contains a constructor.
1704        assert!(program.contains_constructor());
1705        assert_eq!(program.constructor().unwrap().commands().len(), 5);
1706    }
1707
1708    #[test]
1709    fn test_program_equality_and_checksum() {
1710        fn run_test(program1: &str, program2: &str, expected_equal: bool) {
1711            println!("Comparing programs:\n{program1}\n{program2}");
1712            let program1 = Program::<CurrentNetwork>::from_str(program1).unwrap();
1713            let program2 = Program::<CurrentNetwork>::from_str(program2).unwrap();
1714            assert_eq!(program1 == program2, expected_equal);
1715            assert_eq!(program1.to_checksum() == program2.to_checksum(), expected_equal);
1716        }
1717
1718        // Test two identical programs, with different whitespace.
1719        run_test(r"program test.aleo; function dummy:    ", r"program  test.aleo;     function dummy:   ", true);
1720
1721        // Test two programs, one with a different function name.
1722        run_test(r"program test.aleo; function dummy:    ", r"program test.aleo; function bummy:   ", false);
1723
1724        // Test two programs, one with a constructor and one without.
1725        run_test(
1726            r"program test.aleo; function dummy:    ",
1727            r"program test.aleo; constructor: assert.eq true true; function dummy: ",
1728            false,
1729        );
1730
1731        // Test two programs, both with a struct and function, but in different order.
1732        run_test(
1733            r"program test.aleo; struct foo: data as u8; function dummy:",
1734            r"program test.aleo; function dummy: struct foo: data as u8;",
1735            false,
1736        );
1737    }
1738
1739    #[test]
1740    fn test_contains_v14_syntax() -> Result<()> {
1741        // A baseline program with no V14 syntax.
1742        let no_v14 = Program::<CurrentNetwork>::from_str(
1743            r"program test.aleo;
1744function foo:
1745    input r0 as u64.public;
1746    output r0 as u64.public;",
1747        )?;
1748        assert!(!no_v14.contains_v14_syntax()?);
1749
1750        // A program with `dynamic.record` as a function input type.
1751        let dynamic_record_input = Program::<CurrentNetwork>::from_str(
1752            r"program test.aleo;
1753function foo:
1754    input r0 as dynamic.record;",
1755        )?;
1756        assert!(dynamic_record_input.contains_v14_syntax()?);
1757
1758        // A program with `dynamic.future` as a function output type.
1759        let dynamic_future_output = Program::<CurrentNetwork>::from_str(
1760            r"program test.aleo;
1761function foo:
1762    output r0 as dynamic.future;",
1763        )?;
1764        assert!(dynamic_future_output.contains_v14_syntax()?);
1765
1766        // A program with a `call.dynamic` instruction.
1767        let call_dynamic = Program::<CurrentNetwork>::from_str(
1768            r"program test.aleo;
1769function foo:
1770    input r0 as field.public;
1771    input r1 as field.public;
1772    input r2 as field.public;
1773    call.dynamic r0 r1 r2 into r3 (as u64.public);
1774    output r3 as u64.public;",
1775        )?;
1776        assert!(call_dynamic.contains_v14_syntax()?);
1777
1778        // A program with a `get.record.dynamic` instruction.
1779        let get_record_dynamic = Program::<CurrentNetwork>::from_str(
1780            r"program test.aleo;
1781function foo:
1782    input r0 as dynamic.record;
1783    get.record.dynamic r0.amount into r1 as field;
1784    output r1 as field.public;",
1785        )?;
1786        assert!(get_record_dynamic.contains_v14_syntax()?);
1787
1788        // A program with a `cast ... as dynamic.record` instruction.
1789        let cast_dynamic_record = Program::<CurrentNetwork>::from_str(
1790            r"program test.aleo;
1791record token:
1792    owner as address.private;
1793    amount as u64.private;
1794function foo:
1795    input r0 as token.record;
1796    cast r0 into r1 as dynamic.record;
1797    output r0.owner as address.private;",
1798        )?;
1799        assert!(cast_dynamic_record.contains_v14_syntax()?);
1800
1801        // A program with `contains.dynamic` in a finalize block.
1802        let contains_dynamic = Program::<CurrentNetwork>::from_str(
1803            r"program test.aleo;
1804function bar:
1805    input r0 as field.public;
1806    input r1 as field.public;
1807    input r2 as field.public;
1808    input r3 as field.public;
1809    async bar r0 r1 r2 r3 into r4;
1810    output r4 as test.aleo/bar.future;
1811finalize bar:
1812    input r0 as field.public;
1813    input r1 as field.public;
1814    input r2 as field.public;
1815    input r3 as field.public;
1816    contains.dynamic r0 r1 r2[r3] into r4;",
1817        )?;
1818        assert!(contains_dynamic.contains_v14_syntax()?);
1819
1820        // A program with `get.dynamic` in a finalize block.
1821        let get_dynamic = Program::<CurrentNetwork>::from_str(
1822            r"program test.aleo;
1823function bar:
1824    input r0 as field.public;
1825    input r1 as field.public;
1826    input r2 as field.public;
1827    input r3 as field.public;
1828    async bar r0 r1 r2 r3 into r4;
1829    output r4 as test.aleo/bar.future;
1830finalize bar:
1831    input r0 as field.public;
1832    input r1 as field.public;
1833    input r2 as field.public;
1834    input r3 as field.public;
1835    get.dynamic r0 r1 r2[r3] into r4 as field;",
1836        )?;
1837        assert!(get_dynamic.contains_v14_syntax()?);
1838
1839        // A program with `dynamic.future` as a finalize input type.
1840        let dynamic_future_finalize_input = Program::<CurrentNetwork>::from_str(
1841            r"program test.aleo;
1842function foo:
1843    input r0 as field.public;
1844    async foo r0 into r1;
1845    output r1 as test.aleo/foo.future;
1846finalize foo:
1847    input r0 as dynamic.future;
1848    await r0;",
1849        )?;
1850        assert!(dynamic_future_finalize_input.contains_v14_syntax()?);
1851
1852        // A program with `dynamic.record` as a closure input type.
1853        let closure_dynamic_input = Program::<CurrentNetwork>::from_str(
1854            r"program test.aleo;
1855closure bar:
1856    input r0 as dynamic.record;
1857    input r1 as field;
1858    add r1 r1 into r2;",
1859        )?;
1860        assert!(closure_dynamic_input.contains_v14_syntax()?);
1861
1862        // A program with `dynamic.record` as a closure output type.
1863        let closure_dynamic_output = Program::<CurrentNetwork>::from_str(
1864            r"program test.aleo;
1865closure bar:
1866    input r0 as field;
1867    add r0 r0 into r1;
1868    output r1 as dynamic.record;",
1869        )?;
1870        assert!(closure_dynamic_output.contains_v14_syntax()?);
1871
1872        // A program with `get.or_use.dynamic` in a finalize block.
1873        let get_or_use_dynamic = Program::<CurrentNetwork>::from_str(
1874            r"program test.aleo;
1875function bar:
1876    input r0 as field.public;
1877    input r1 as field.public;
1878    input r2 as field.public;
1879    input r3 as field.public;
1880    input r4 as u64.public;
1881    async bar r0 r1 r2 r3 r4 into r5;
1882    output r5 as test.aleo/bar.future;
1883finalize bar:
1884    input r0 as field.public;
1885    input r1 as field.public;
1886    input r2 as field.public;
1887    input r3 as field.public;
1888    input r4 as u64.public;
1889    get.or_use.dynamic r0 r1 r2[r3] r4 into r5 as u64;",
1890        )?;
1891        assert!(get_or_use_dynamic.contains_v14_syntax()?);
1892
1893        // A program with `snark.verify` in a finalize block.
1894        let snark_verify = Program::<CurrentNetwork>::from_str(
1895            r"program test.aleo;
1896function foo:
1897    input r0 as [u8; 8u32].public;
1898    input r1 as [field; 1u32].public;
1899    input r2 as [u8; 8u32].public;
1900    async foo r0 r1 r2 into r3;
1901    output r3 as test.aleo/foo.future;
1902finalize foo:
1903    input r0 as [u8; 8u32].public;
1904    input r1 as [field; 1u32].public;
1905    input r2 as [u8; 8u32].public;
1906    snark.verify r0 1u8 r1 r2 into r3;",
1907        )?;
1908        assert!(snark_verify.contains_v14_syntax()?);
1909
1910        // A program with `snark.verify.batch` in a finalize block.
1911        let snark_verify_batch = Program::<CurrentNetwork>::from_str(
1912            r"program test.aleo;
1913function foo:
1914    input r0 as [[u8; 8u32]; 1u32].public;
1915    input r1 as [[[field; 1u32]; 1u32]; 1u32].public;
1916    input r2 as [u8; 8u32].public;
1917    async foo r0 r1 r2 into r3;
1918    output r3 as test.aleo/foo.future;
1919finalize foo:
1920    input r0 as [[u8; 8u32]; 1u32].public;
1921    input r1 as [[[field; 1u32]; 1u32]; 1u32].public;
1922    input r2 as [u8; 8u32].public;
1923    snark.verify.batch r0 1u8 r1 r2 into r3;",
1924        )?;
1925        assert!(snark_verify_batch.contains_v14_syntax()?);
1926
1927        // A program with `aleo::GENERATOR` as an operand.
1928        let aleo_generator = Program::<CurrentNetwork>::from_str(
1929            r"program test.aleo;
1930function foo:
1931    input r0 as scalar.public;
1932    mul aleo::GENERATOR r0 into r1;
1933    output r1 as group.public;",
1934        )?;
1935        assert!(aleo_generator.contains_v14_syntax()?);
1936
1937        // A program with `aleo::GENERATOR_POWERS` as an operand.
1938        let aleo_generator_powers = Program::<CurrentNetwork>::from_str(
1939            r"program test.aleo;
1940function foo:
1941    input r0 as scalar.public;
1942    mul aleo::GENERATOR_POWERS[0u32] r0 into r1;
1943    output r1 as group.public;",
1944        )?;
1945        assert!(aleo_generator_powers.contains_v14_syntax()?);
1946
1947        // A program with `dynamic.future` as a closure output type.
1948        let closure_dynamic_future_output = Program::<CurrentNetwork>::from_str(
1949            r"program test.aleo;
1950closure bar:
1951    input r0 as field;
1952    add r0 r0 into r1;
1953    output r1 as dynamic.future;",
1954        )?;
1955        assert!(closure_dynamic_future_output.contains_v14_syntax()?);
1956
1957        // A program with a V14 opcode (`aleo::GENERATOR`) in a constructor block.
1958        let constructor_v14 = Program::<CurrentNetwork>::from_str(
1959            r"program test.aleo;
1960function dummy:
1961    input r0 as field.public;
1962    output r0 as field.public;
1963constructor:
1964    assert.eq aleo::GENERATOR aleo::GENERATOR;",
1965        )?;
1966        assert!(constructor_v14.contains_v14_syntax()?);
1967
1968        Ok(())
1969    }
1970}