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