1#![forbid(unsafe_code)]
17#![allow(clippy::too_many_arguments)]
18#![warn(clippy::cast_possible_truncation)]
19
20extern crate snarkvm_circuit as circuit;
21extern crate snarkvm_console as console;
22
23pub type Program<N> = crate::ProgramCore<N>;
24pub type Function<N> = crate::FunctionCore<N>;
25pub type Finalize<N> = crate::FinalizeCore<N>;
26pub type Closure<N> = crate::ClosureCore<N>;
27pub type Constructor<N> = crate::ConstructorCore<N>;
28
29mod closure;
30pub use closure::*;
31
32mod constructor;
33pub use constructor::*;
34
35pub mod finalize;
36pub use finalize::*;
37
38mod function;
39pub use function::*;
40
41mod import;
42pub use import::*;
43
44pub mod logic;
45pub use logic::*;
46
47mod mapping;
48pub use mapping::*;
49
50mod traits;
51pub use traits::*;
52
53mod bytes;
54mod parse;
55mod serialize;
56mod to_checksum;
57
58use console::{
59 network::{
60 ConsensusVersion,
61 consensus_config_value,
62 prelude::{
63 Debug,
64 Deserialize,
65 Deserializer,
66 Display,
67 Err,
68 Error,
69 ErrorKind,
70 Formatter,
71 FromBytes,
72 FromBytesDeserializer,
73 FromStr,
74 IoResult,
75 Itertools,
76 Network,
77 Parser,
78 ParserResult,
79 Read,
80 Result,
81 Sanitizer,
82 Serialize,
83 Serializer,
84 ToBytes,
85 ToBytesSerializer,
86 TypeName,
87 Write,
88 anyhow,
89 bail,
90 de,
91 ensure,
92 error,
93 fmt,
94 make_error,
95 many0,
96 many1,
97 map,
98 map_res,
99 tag,
100 take,
101 },
102 },
103 program::{
104 EntryType,
105 FinalizeType,
106 Identifier,
107 Locator,
108 PlaintextType,
109 ProgramID,
110 RecordType,
111 RegisterType,
112 StructType,
113 ValueType,
114 },
115 types::U8,
116};
117use snarkvm_utilities::{cfg_find_map, cfg_iter};
118
119use indexmap::{IndexMap, IndexSet};
120use std::collections::BTreeSet;
121use tiny_keccak::{Hasher, Sha3 as TinySha3};
122
123#[derive(Copy, Clone, PartialEq, Eq, Hash)]
124enum ProgramLabel<N: Network> {
125 Constructor,
127 Identifier(Identifier<N>),
129}
130
131#[cfg(not(feature = "serial"))]
132use rayon::prelude::*;
133
134#[derive(Copy, Clone, PartialEq, Eq, Hash)]
135enum ProgramDefinition {
136 Constructor,
138 Mapping,
140 Struct,
142 Record,
144 Closure,
146 Function,
148}
149
150#[derive(Clone)]
151pub struct ProgramCore<N: Network> {
152 id: ProgramID<N>,
154 imports: IndexMap<ProgramID<N>, Import<N>>,
156 components: IndexMap<ProgramLabel<N>, ProgramDefinition>,
158 constructor: Option<ConstructorCore<N>>,
160 mappings: IndexMap<Identifier<N>, Mapping<N>>,
162 structs: IndexMap<Identifier<N>, StructType<N>>,
164 records: IndexMap<Identifier<N>, RecordType<N>>,
166 closures: IndexMap<Identifier<N>, ClosureCore<N>>,
168 functions: IndexMap<Identifier<N>, FunctionCore<N>>,
170}
171
172impl<N: Network> PartialEq for ProgramCore<N> {
173 fn eq(&self, other: &Self) -> bool {
176 if self.components.len() != other.components.len() {
178 return false;
179 }
180 for (left, right) in self.components.iter().zip_eq(other.components.iter()) {
182 if left != right {
183 return false;
184 }
185 }
186 self.id == other.id
188 && self.imports == other.imports
189 && self.mappings == other.mappings
190 && self.structs == other.structs
191 && self.records == other.records
192 && self.closures == other.closures
193 && self.functions == other.functions
194 }
195}
196
197impl<N: Network> Eq for ProgramCore<N> {}
198
199impl<N: Network> ProgramCore<N> {
200 #[rustfmt::skip]
204 pub const KEYWORDS: &'static [&'static str] = &[
205 "const",
207 "constant",
208 "public",
209 "private",
210 "address",
212 "boolean",
213 "field",
214 "group",
215 "i8",
216 "i16",
217 "i32",
218 "i64",
219 "i128",
220 "u8",
221 "u16",
222 "u32",
223 "u64",
224 "u128",
225 "scalar",
226 "signature",
227 "string",
228 "true",
230 "false",
231 "input",
233 "output",
234 "as",
235 "into",
236 "record",
238 "owner",
239 "transition",
241 "import",
242 "function",
243 "struct",
244 "closure",
245 "program",
246 "aleo",
247 "self",
248 "storage",
249 "mapping",
250 "key",
251 "value",
252 "async",
253 "finalize",
254 "global",
256 "block",
257 "return",
258 "break",
259 "assert",
260 "continue",
261 "let",
262 "if",
263 "else",
264 "while",
265 "for",
266 "switch",
267 "case",
268 "default",
269 "match",
270 "enum",
271 "struct",
272 "union",
273 "trait",
274 "impl",
275 "type",
276 "future",
277 ];
278 #[rustfmt::skip]
283 pub const RESTRICTED_KEYWORDS: &'static [(ConsensusVersion, &'static [&'static str])] = &[
284 (ConsensusVersion::V6, &["constructor"]),
285 (ConsensusVersion::V14, &["dynamic", "identifier"]),
286 ];
287
288 #[inline]
290 pub fn new(id: ProgramID<N>) -> Result<Self> {
291 ensure!(!Self::is_reserved_keyword(id.name()), "Program name is invalid: {}", id.name());
293
294 Ok(Self {
295 id,
296 imports: IndexMap::new(),
297 constructor: None,
298 components: IndexMap::new(),
299 mappings: IndexMap::new(),
300 structs: IndexMap::new(),
301 records: IndexMap::new(),
302 closures: IndexMap::new(),
303 functions: IndexMap::new(),
304 })
305 }
306
307 #[inline]
309 pub fn credits() -> Result<Self> {
310 Self::from_str(include_str!("./resources/credits.aleo"))
311 }
312
313 pub const fn id(&self) -> &ProgramID<N> {
315 &self.id
316 }
317
318 pub const fn imports(&self) -> &IndexMap<ProgramID<N>, Import<N>> {
320 &self.imports
321 }
322
323 pub const fn constructor(&self) -> Option<&ConstructorCore<N>> {
325 self.constructor.as_ref()
326 }
327
328 pub const fn mappings(&self) -> &IndexMap<Identifier<N>, Mapping<N>> {
330 &self.mappings
331 }
332
333 pub const fn structs(&self) -> &IndexMap<Identifier<N>, StructType<N>> {
335 &self.structs
336 }
337
338 pub const fn records(&self) -> &IndexMap<Identifier<N>, RecordType<N>> {
340 &self.records
341 }
342
343 pub const fn closures(&self) -> &IndexMap<Identifier<N>, ClosureCore<N>> {
345 &self.closures
346 }
347
348 pub const fn functions(&self) -> &IndexMap<Identifier<N>, FunctionCore<N>> {
350 &self.functions
351 }
352
353 pub fn contains_import(&self, id: &ProgramID<N>) -> bool {
355 self.imports.contains_key(id)
356 }
357
358 pub const fn contains_constructor(&self) -> bool {
360 self.constructor.is_some()
361 }
362
363 pub fn contains_mapping(&self, name: &Identifier<N>) -> bool {
365 self.mappings.contains_key(name)
366 }
367
368 pub fn contains_struct(&self, name: &Identifier<N>) -> bool {
370 self.structs.contains_key(name)
371 }
372
373 pub fn contains_record(&self, name: &Identifier<N>) -> bool {
375 self.records.contains_key(name)
376 }
377
378 pub fn contains_closure(&self, name: &Identifier<N>) -> bool {
380 self.closures.contains_key(name)
381 }
382
383 pub fn contains_function(&self, name: &Identifier<N>) -> bool {
385 self.functions.contains_key(name)
386 }
387
388 pub fn get_mapping(&self, name: &Identifier<N>) -> Result<Mapping<N>> {
390 let mapping = self.mappings.get(name).cloned().ok_or_else(|| anyhow!("Mapping '{name}' is not defined."))?;
392 ensure!(mapping.name() == name, "Expected mapping '{name}', but found mapping '{}'", mapping.name());
394 Ok(mapping)
396 }
397
398 pub fn get_struct(&self, name: &Identifier<N>) -> Result<&StructType<N>> {
400 let struct_ = self.structs.get(name).ok_or_else(|| anyhow!("Struct '{name}' is not defined."))?;
402 ensure!(struct_.name() == name, "Expected struct '{name}', but found struct '{}'", struct_.name());
404 ensure!(!struct_.members().is_empty(), "Struct '{name}' is missing members.");
406 Ok(struct_)
408 }
409
410 pub fn get_record(&self, name: &Identifier<N>) -> Result<&RecordType<N>> {
412 let record = self.records.get(name).ok_or_else(|| anyhow!("Record '{name}' is not defined."))?;
414 ensure!(record.name() == name, "Expected record '{name}', but found record '{}'", record.name());
416 Ok(record)
418 }
419
420 pub fn get_closure(&self, name: &Identifier<N>) -> Result<ClosureCore<N>> {
422 let closure = self.closures.get(name).cloned().ok_or_else(|| anyhow!("Closure '{name}' is not defined."))?;
424 ensure!(closure.name() == name, "Expected closure '{name}', but found closure '{}'", closure.name());
426 ensure!(!closure.inputs().is_empty(), "Cannot evaluate a closure without input statements");
428 ensure!(closure.inputs().len() <= N::MAX_INPUTS, "Closure exceeds maximum number of inputs");
430 ensure!(!closure.instructions().is_empty(), "Cannot evaluate a closure without instructions");
432 ensure!(closure.outputs().len() <= N::MAX_OUTPUTS, "Closure exceeds maximum number of outputs");
434 Ok(closure)
436 }
437
438 pub fn get_function(&self, name: &Identifier<N>) -> Result<FunctionCore<N>> {
440 self.get_function_ref(name).cloned()
441 }
442
443 pub fn get_function_ref(&self, name: &Identifier<N>) -> Result<&FunctionCore<N>> {
445 let function = self.functions.get(name).ok_or(anyhow!("Function '{}/{name}' is not defined.", self.id))?;
447 ensure!(function.name() == name, "Expected function '{name}', but found function '{}'", function.name());
449 ensure!(function.inputs().len() <= N::MAX_INPUTS, "Function exceeds maximum number of inputs");
451 ensure!(function.instructions().len() <= N::MAX_INSTRUCTIONS, "Function exceeds maximum instructions");
453 ensure!(function.outputs().len() <= N::MAX_OUTPUTS, "Function exceeds maximum number of outputs");
455 Ok(function)
457 }
458
459 #[inline]
464 fn add_import(&mut self, import: Import<N>) -> Result<()> {
465 let import_name = *import.name();
467
468 ensure!(self.imports.len() < N::MAX_IMPORTS, "Program exceeds the maximum number of imports");
470
471 ensure!(self.is_unique_name(&import_name), "'{import_name}' is already in use.");
473 ensure!(!Self::is_reserved_opcode(&import_name.to_string()), "'{import_name}' is a reserved opcode.");
475 ensure!(!Self::is_reserved_keyword(&import_name), "'{import_name}' is a reserved keyword.");
477
478 ensure!(
480 !self.imports.contains_key(import.program_id()),
481 "Import '{}' is already defined.",
482 import.program_id()
483 );
484
485 if self.imports.insert(*import.program_id(), import.clone()).is_some() {
487 bail!("'{}' already exists in the program.", import.program_id())
488 }
489 Ok(())
490 }
491
492 fn add_constructor(&mut self, constructor: ConstructorCore<N>) -> Result<()> {
498 ensure!(self.constructor.is_none(), "Program already has a constructor.");
500 ensure!(!constructor.commands().is_empty(), "Constructor must have at least one command");
502 ensure!(constructor.commands().len() <= N::MAX_COMMANDS, "Constructor exceeds maximum number of commands");
503 if self.components.insert(ProgramLabel::Constructor, ProgramDefinition::Constructor).is_some() {
505 bail!("Constructor already exists in the program.")
506 }
507 self.constructor = Some(constructor);
509 Ok(())
510 }
511
512 #[inline]
518 fn add_mapping(&mut self, mapping: Mapping<N>) -> Result<()> {
519 let mapping_name = *mapping.name();
521
522 ensure!(self.mappings.len() < N::MAX_MAPPINGS, "Program exceeds the maximum number of mappings");
524
525 ensure!(self.is_unique_name(&mapping_name), "'{mapping_name}' is already in use.");
527 ensure!(!Self::is_reserved_keyword(&mapping_name), "'{mapping_name}' is a reserved keyword.");
529 ensure!(!Self::is_reserved_opcode(&mapping_name.to_string()), "'{mapping_name}' is a reserved opcode.");
531
532 if self.components.insert(ProgramLabel::Identifier(mapping_name), ProgramDefinition::Mapping).is_some() {
534 bail!("'{mapping_name}' already exists in the program.")
535 }
536 if self.mappings.insert(mapping_name, mapping).is_some() {
538 bail!("'{mapping_name}' already exists in the program.")
539 }
540 Ok(())
541 }
542
543 #[inline]
551 fn add_struct(&mut self, struct_: StructType<N>) -> Result<()> {
552 let struct_name = *struct_.name();
554
555 ensure!(self.structs.len() < N::MAX_STRUCTS, "Program exceeds the maximum number of structs.");
557
558 ensure!(self.is_unique_name(&struct_name), "'{struct_name}' is already in use.");
560 ensure!(!Self::is_reserved_opcode(&struct_name.to_string()), "'{struct_name}' is a reserved opcode.");
562 ensure!(!Self::is_reserved_keyword(&struct_name), "'{struct_name}' is a reserved keyword.");
564
565 ensure!(!struct_.members().is_empty(), "Struct '{struct_name}' is missing members.");
567
568 for (identifier, plaintext_type) in struct_.members() {
571 ensure!(!Self::is_reserved_keyword(identifier), "'{identifier}' is a reserved keyword.");
573 match plaintext_type {
575 PlaintextType::Literal(_) => continue,
576 PlaintextType::Struct(member_identifier) => {
577 if !self.structs.contains_key(member_identifier) {
579 bail!("'{member_identifier}' in struct '{struct_name}' is not defined.")
580 }
581 }
582 PlaintextType::ExternalStruct(locator) => {
583 if !self.imports.contains_key(locator.program_id()) {
584 bail!(
585 "External program {} referenced in struct '{struct_name}' does not exist",
586 locator.program_id()
587 );
588 }
589 }
590 PlaintextType::Array(array_type) => {
591 match array_type.base_element_type() {
592 PlaintextType::Struct(struct_name) =>
593 {
595 if !self.structs.contains_key(struct_name) {
596 bail!("'{struct_name}' in array '{array_type}' is not defined.")
597 }
598 }
599 PlaintextType::ExternalStruct(locator) => {
600 if !self.imports.contains_key(locator.program_id()) {
601 bail!(
602 "External program {} in array '{array_type}' does not exist",
603 locator.program_id()
604 );
605 }
606 }
607 PlaintextType::Array(..) | PlaintextType::Literal(..) => {}
608 }
609 }
610 }
611 }
612
613 if self.components.insert(ProgramLabel::Identifier(struct_name), ProgramDefinition::Struct).is_some() {
615 bail!("'{struct_name}' already exists in the program.")
616 }
617 if self.structs.insert(struct_name, struct_).is_some() {
619 bail!("'{struct_name}' already exists in the program.")
620 }
621 Ok(())
622 }
623
624 #[inline]
632 fn add_record(&mut self, record: RecordType<N>) -> Result<()> {
633 let record_name = *record.name();
635
636 ensure!(self.records.len() < N::MAX_RECORDS, "Program exceeds the maximum number of records.");
638
639 ensure!(self.is_unique_name(&record_name), "'{record_name}' is already in use.");
641 ensure!(!Self::is_reserved_opcode(&record_name.to_string()), "'{record_name}' is a reserved opcode.");
643 ensure!(!Self::is_reserved_keyword(&record_name), "'{record_name}' is a reserved keyword.");
645
646 for (identifier, entry_type) in record.entries() {
649 ensure!(!Self::is_reserved_keyword(identifier), "'{identifier}' is a reserved keyword.");
651 match entry_type.plaintext_type() {
653 PlaintextType::Literal(_) => continue,
654 PlaintextType::Struct(identifier) => {
655 if !self.structs.contains_key(identifier) {
656 bail!("Struct '{identifier}' in record '{record_name}' is not defined.")
657 }
658 }
659 PlaintextType::ExternalStruct(locator) => {
660 if !self.imports.contains_key(locator.program_id()) {
661 bail!(
662 "External program {} referenced in record '{record_name}' does not exist",
663 locator.program_id()
664 );
665 }
666 }
667 PlaintextType::Array(array_type) => {
668 match array_type.base_element_type() {
669 PlaintextType::Struct(struct_name) =>
670 {
672 if !self.structs.contains_key(struct_name) {
673 bail!("'{struct_name}' in array '{array_type}' is not defined.")
674 }
675 }
676 PlaintextType::ExternalStruct(locator) => {
677 if !self.imports.contains_key(locator.program_id()) {
678 bail!(
679 "External program {} in array '{array_type}' does not exist",
680 locator.program_id()
681 );
682 }
683 }
684 PlaintextType::Array(..) | PlaintextType::Literal(..) => {}
685 }
686 }
687 }
688 }
689
690 if self.components.insert(ProgramLabel::Identifier(record_name), ProgramDefinition::Record).is_some() {
692 bail!("'{record_name}' already exists in the program.")
693 }
694 if self.records.insert(record_name, record).is_some() {
696 bail!("'{record_name}' already exists in the program.")
697 }
698 Ok(())
699 }
700
701 #[inline]
715 fn add_closure(&mut self, closure: ClosureCore<N>) -> Result<()> {
716 let closure_name = *closure.name();
718
719 ensure!(self.closures.len() < N::MAX_CLOSURES, "Program exceeds the maximum number of closures.");
721
722 ensure!(self.is_unique_name(&closure_name), "'{closure_name}' is already in use.");
724 ensure!(!Self::is_reserved_opcode(&closure_name.to_string()), "'{closure_name}' is a reserved opcode.");
726 ensure!(!Self::is_reserved_keyword(&closure_name), "'{closure_name}' is a reserved keyword.");
728
729 ensure!(!closure.inputs().is_empty(), "Cannot evaluate a closure without input statements");
731 ensure!(closure.inputs().len() <= N::MAX_INPUTS, "Closure exceeds maximum number of inputs");
733 ensure!(!closure.instructions().is_empty(), "Cannot evaluate a closure without instructions");
735 ensure!(closure.outputs().len() <= N::MAX_OUTPUTS, "Closure exceeds maximum number of outputs");
737
738 if self.components.insert(ProgramLabel::Identifier(closure_name), ProgramDefinition::Closure).is_some() {
740 bail!("'{closure_name}' already exists in the program.")
741 }
742 if self.closures.insert(closure_name, closure).is_some() {
744 bail!("'{closure_name}' already exists in the program.")
745 }
746 Ok(())
747 }
748
749 #[inline]
763 fn add_function(&mut self, function: FunctionCore<N>) -> Result<()> {
764 let function_name = *function.name();
766
767 ensure!(self.functions.len() < N::MAX_FUNCTIONS, "Program exceeds the maximum number of functions");
769
770 ensure!(self.is_unique_name(&function_name), "'{function_name}' is already in use.");
772 ensure!(!Self::is_reserved_opcode(&function_name.to_string()), "'{function_name}' is a reserved opcode.");
774 ensure!(!Self::is_reserved_keyword(&function_name), "'{function_name}' is a reserved keyword.");
776
777 ensure!(function.inputs().len() <= N::MAX_INPUTS, "Function exceeds maximum number of inputs");
779 ensure!(function.instructions().len() <= N::MAX_INSTRUCTIONS, "Function exceeds maximum instructions");
781 ensure!(function.outputs().len() <= N::MAX_OUTPUTS, "Function exceeds maximum number of outputs");
783
784 if self.components.insert(ProgramLabel::Identifier(function_name), ProgramDefinition::Function).is_some() {
786 bail!("'{function_name}' already exists in the program.")
787 }
788 if self.functions.insert(function_name, function).is_some() {
790 bail!("'{function_name}' already exists in the program.")
791 }
792 Ok(())
793 }
794
795 fn is_unique_name(&self, name: &Identifier<N>) -> bool {
797 !self.components.contains_key(&ProgramLabel::Identifier(*name))
798 }
799
800 pub fn is_reserved_opcode(name: &str) -> bool {
802 Instruction::<N>::is_reserved_opcode(name)
803 }
804
805 pub fn is_reserved_keyword(name: &Identifier<N>) -> bool {
807 let name = name.to_string();
809 Self::KEYWORDS.iter().any(|keyword| *keyword == name)
811 }
812
813 pub fn restricted_keywords_for_consensus_version(
815 consensus_version: ConsensusVersion,
816 ) -> impl Iterator<Item = &'static str> {
817 Self::RESTRICTED_KEYWORDS
818 .iter()
819 .filter(move |(version, _)| *version <= consensus_version)
820 .flat_map(|(_, keywords)| *keywords)
821 .copied()
822 }
823
824 pub fn check_restricted_keywords_for_consensus_version(&self, consensus_version: ConsensusVersion) -> Result<()> {
828 let keywords =
830 Program::<N>::restricted_keywords_for_consensus_version(consensus_version).collect::<IndexSet<_>>();
831 let program_name = self.id().name().to_string();
833 if keywords.contains(&program_name.as_str()) {
834 bail!("Program name '{program_name}' is a restricted keyword for the current consensus version")
835 }
836 for component in self.components.keys() {
838 match component {
839 ProgramLabel::Identifier(identifier) => {
840 if keywords.contains(identifier.to_string().as_str()) {
841 bail!(
842 "Program component '{identifier}' is a restricted keyword for the current consensus version"
843 )
844 }
845 }
846 ProgramLabel::Constructor => continue,
847 }
848 }
849 for record_type in self.records().values() {
851 for entry_name in record_type.entries().keys() {
852 if keywords.contains(entry_name.to_string().as_str()) {
853 bail!("Record entry '{entry_name}' is a restricted keyword for the current consensus version")
854 }
855 }
856 }
857 for struct_type in self.structs().values() {
859 for member_name in struct_type.members().keys() {
860 if keywords.contains(member_name.to_string().as_str()) {
861 bail!("Struct member '{member_name}' is a restricted keyword for the current consensus version")
862 }
863 }
864 }
865 for function in self.functions().values() {
869 if let Some(finalize_logic) = function.finalize_logic() {
870 for position in finalize_logic.positions().keys() {
871 if keywords.contains(position.to_string().as_str()) {
872 bail!(
873 "Finalize position '{position}' is a restricted keyword for the current consensus version"
874 )
875 }
876 }
877 }
878 }
879 Ok(())
880 }
881
882 pub fn check_program_naming_structure(&self) -> Result<()> {
888 let program_id = self.id().name().to_string();
890 if program_id.contains("aleo") {
891 bail!("Program ID '{program_id}' can't contain the reserved keyword 'aleo'.");
892 }
893
894 let record_names: BTreeSet<String> = self.records.keys().map(|name| name.to_string()).collect();
896
897 for record_name in &record_names {
899 if record_name.contains("aleo") {
900 bail!("Record name '{record_name}' can't contain the reserved keyword 'aleo'.");
901 }
902 }
903
904 let mut record_names_iter = record_names.iter();
906 let mut previous_record_name = record_names_iter.next();
907 for record_name in record_names_iter {
908 if let Some(previous) = previous_record_name {
909 if record_name.starts_with(previous) {
910 bail!("Record name '{previous}' can't be a prefix of record name '{record_name}'.");
911 }
912 }
913 previous_record_name = Some(record_name);
914 }
915
916 for record_entry_name in self.records.values().flat_map(|record_type| record_type.entries().keys()) {
918 if record_entry_name.to_string().contains("aleo") {
919 bail!("Record entry name '{record_entry_name}' can't contain the reserved keyword 'aleo'.");
920 }
921 }
922
923 Ok(())
924 }
925
926 pub fn check_external_calls_to_credits_upgrade(&self) -> Result<()> {
928 cfg_iter!(self.functions()).flat_map(|(_, function)| function.instructions()).try_for_each(|instruction| {
930 if let Some(CallOperator::Locator(locator)) = instruction.call_operator() {
931 if locator.to_string() == "credits.aleo/upgrade" {
933 bail!("External call to restricted locator '{locator}'")
934 }
935 }
936 Ok(())
937 })?;
938 Ok(())
939 }
940
941 #[inline]
945 pub fn contains_v9_syntax(&self) -> bool {
946 if self.contains_constructor() {
948 return true;
949 }
950 for function in self.functions().values() {
953 if let Some(finalize_logic) = function.finalize_logic() {
955 for command in finalize_logic.commands() {
957 for operand in command.operands() {
958 if matches!(operand, Operand::Checksum(_) | Operand::Edition(_) | Operand::ProgramOwner(_)) {
959 return true;
960 }
961 }
962 }
963 }
964 }
965 false
967 }
968
969 pub fn contains_external_struct(&self) -> bool {
974 self.mappings.values().any(|mapping| mapping.contains_external_struct())
975 || self
976 .structs
977 .values()
978 .flat_map(|struct_| struct_.members().values())
979 .any(|plaintext_type| plaintext_type.contains_external_struct())
980 || self
981 .records
982 .values()
983 .flat_map(|record| record.entries().values())
984 .any(|entry| entry.plaintext_type().contains_external_struct())
985 || self.closures.values().any(|closure| closure.contains_external_struct())
986 || self.functions.values().any(|function| function.contains_external_struct())
987 || self.constructor.iter().any(|constructor| constructor.contains_external_struct())
988 }
989
990 pub fn violates_pre_v13_external_record_and_future_rules<F0, F1, F2, F3>(
1002 &self,
1003 get_external_record: &F0,
1004 get_external_function: &F1,
1005 get_external_future: &F2,
1006 is_local_struct: &F3,
1007 ) -> bool
1008 where
1009 F0: Fn(&Locator<N>) -> Result<RecordType<N>>,
1010 F1: Fn(&Locator<N>) -> Result<FunctionCore<N>>,
1011 F2: Fn(&Locator<N>) -> Result<FinalizeCore<N>>,
1012 F3: Fn(&Identifier<N>) -> bool,
1013 {
1014 fn plaintext_uses_nonlocal_struct<N: Network>(
1016 ty: &PlaintextType<N>,
1017 is_local_struct: &impl Fn(&Identifier<N>) -> bool,
1018 ) -> bool {
1019 match ty {
1020 PlaintextType::Struct(name) => !is_local_struct(name),
1021 PlaintextType::Array(array_type) => {
1022 plaintext_uses_nonlocal_struct(array_type.base_element_type(), is_local_struct)
1023 }
1024 _ => false,
1025 }
1026 }
1027
1028 let record_uses_nonlocal_struct = |record: &RecordType<N>| {
1030 record.entries().iter().any(|(_, member)| match member {
1031 EntryType::Constant(ty) | EntryType::Private(ty) | EntryType::Public(ty) => {
1032 plaintext_uses_nonlocal_struct(ty, is_local_struct)
1033 }
1034 })
1035 };
1036
1037 for function in self.functions.values() {
1038 for input in function.inputs() {
1040 let ValueType::ExternalRecord(locator) = input.value_type() else {
1041 continue;
1042 };
1043 let Ok(record) = get_external_record(locator) else {
1044 continue;
1045 };
1046 if record_uses_nonlocal_struct(&record) {
1047 return true;
1048 }
1049 }
1050
1051 for instruction in function.instructions() {
1053 let Instruction::Call(call) = instruction else { continue };
1054 let CallOperator::Locator(locator) = call.operator() else { continue };
1055 let Ok(external_function) = get_external_function(locator) else {
1056 continue;
1057 };
1058
1059 for output in external_function.outputs() {
1062 match output.value_type() {
1063 ValueType::Record(identifier) => {
1064 let locator = Locator::new(*locator.program_id(), *identifier);
1065 let Ok(record) = get_external_record(&locator) else {
1066 continue;
1067 };
1068 if record_uses_nonlocal_struct(&record) {
1069 return true;
1070 }
1071 }
1072
1073 ValueType::Future(loc) => {
1074 let Ok(future) = get_external_future(loc) else {
1075 continue;
1076 };
1077 for input in future.input_types() {
1078 let FinalizeType::Plaintext(ty) = input else {
1079 continue;
1080 };
1081
1082 if plaintext_uses_nonlocal_struct(&ty, is_local_struct) {
1088 return true;
1089 }
1090 }
1091 }
1092
1093 _ => {}
1094 }
1095 }
1096 }
1097 }
1098
1099 false
1100 }
1101
1102 pub fn exceeds_max_array_size(&self, max_array_size: u32) -> bool {
1104 self.mappings.values().any(|mapping| mapping.exceeds_max_array_size(max_array_size))
1105 || self.structs.values().any(|struct_type| struct_type.exceeds_max_array_size(max_array_size))
1106 || self.records.values().any(|record_type| record_type.exceeds_max_array_size(max_array_size))
1107 || self.closures.values().any(|closure| closure.exceeds_max_array_size(max_array_size))
1108 || self.functions.values().any(|function| function.exceeds_max_array_size(max_array_size))
1109 || self.constructor.iter().any(|constructor| constructor.exceeds_max_array_size(max_array_size))
1110 }
1111
1112 #[inline]
1117 pub fn contains_v11_syntax(&self) -> bool {
1118 let has_op = |opcode: &str| {
1122 opcode.starts_with("ecdsa.verify")
1123 || opcode.starts_with("serialize")
1124 || opcode.starts_with("deserialize")
1125 || opcode.ends_with(".raw")
1126 || opcode.ends_with(".native")
1127 };
1128
1129 let function_contains = cfg_iter!(self.functions())
1131 .flat_map(|(_, function)| function.instructions())
1132 .any(|instruction| has_op(*instruction.opcode()));
1133
1134 let closure_contains = cfg_iter!(self.closures())
1136 .flat_map(|(_, closure)| closure.instructions())
1137 .any(|instruction| has_op(*instruction.opcode()));
1138
1139 let command_contains = cfg_iter!(self.functions())
1141 .flat_map(|(_, function)| function.finalize_logic().map(|finalize| finalize.commands()))
1142 .flatten()
1143 .chain(cfg_iter!(self.constructor).flat_map(|constructor| constructor.commands()))
1144 .any(|command| matches!(command, Command::Instruction(instruction) if has_op(*instruction.opcode())));
1145
1146 function_contains || closure_contains || command_contains
1147 }
1148
1149 #[inline]
1153 pub fn contains_v12_syntax(&self) -> bool {
1154 cfg_iter!(self.functions()).any(|(_, function)| {
1157 function.finalize_logic().is_some_and(|finalize_logic| {
1158 cfg_iter!(finalize_logic.commands()).any(|command| {
1159 cfg_iter!(command.operands()).any(|operand| matches!(operand, Operand::BlockTimestamp))
1160 })
1161 })
1162 })
1163 }
1164
1165 pub fn check_program_size(&self, block_height: u32) -> Result<()> {
1167 let program_size = self.to_string().len();
1169 let maximum_allowed_program_size = consensus_config_value!(N, MAX_PROGRAM_SIZE, block_height)
1171 .ok_or(anyhow!("Failed to fetch maximum program size"))?;
1172
1173 ensure!(
1174 program_size <= maximum_allowed_program_size,
1175 "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 {}).",
1176 N::CONSENSUS_VERSION(block_height)?
1177 );
1178
1179 Ok(())
1180 }
1181
1182 pub fn check_program_writes(&self, block_height: u32) -> Result<()> {
1184 let max_num_writes = consensus_config_value!(N, MAX_WRITES, block_height)
1186 .ok_or(anyhow!("Failed to fetch maximum number of writes"))?;
1187
1188 if self.constructor().is_some_and(|constructor| constructor.num_writes() > max_num_writes) {
1190 bail!(
1191 "Program constructor exceeds the maximum allowed writes ({max_num_writes}) for the current height {block_height} (consensus version {}).",
1192 N::CONSENSUS_VERSION(block_height)?
1193 );
1194 }
1195
1196 if let Some(name) = cfg_find_map!(self.functions(), |function| {
1198 function
1199 .finalize_logic()
1200 .is_some_and(|finalize| finalize.num_writes() > max_num_writes)
1201 .then(|| *function.name())
1202 }) {
1203 bail!(
1204 "Program function '{name}' exceeds the maximum allowed writes ({max_num_writes}) for the current height {block_height} (consensus version {}).",
1205 N::CONSENSUS_VERSION(block_height)?
1206 );
1207 }
1208
1209 Ok(())
1210 }
1211
1212 #[inline]
1228 pub fn contains_v14_syntax(&self) -> Result<bool> {
1229 fn is_v14_instruction<N: Network>(instr: &Instruction<N>) -> bool {
1231 matches!(instr, Instruction::CallDynamic(_) | Instruction::GetRecordDynamic(_))
1232 || matches!(instr, Instruction::Cast(cast) if *cast.cast_type() == CastType::DynamicRecord)
1233 || instr.opcode().starts_with("snark.verify")
1234 || cfg_iter!(instr.operands()).any(|operand| {
1235 matches!(
1236 operand,
1237 Operand::AleoGenerator
1238 | Operand::AleoGeneratorPowers(_)
1239 | Operand::Literal(console::program::Literal::Identifier(..))
1240 )
1241 })
1242 }
1243
1244 fn is_v14_command<N: Network>(cmd: &Command<N>) -> bool {
1246 matches!(cmd, Command::ContainsDynamic(_) | Command::GetDynamic(_) | Command::GetOrUseDynamic(_))
1247 || matches!(cmd, Command::Instruction(instr) if is_v14_instruction(instr))
1248 }
1249
1250 let is_dynamic_value_type =
1252 |vt: &ValueType<N>| matches!(vt, ValueType::DynamicRecord | ValueType::DynamicFuture);
1253
1254 let is_dynamic_register_type =
1256 |rt: &RegisterType<N>| matches!(rt, RegisterType::DynamicRecord | RegisterType::DynamicFuture);
1257
1258 for (_, function) in self.functions() {
1260 if function.instructions().iter().any(is_v14_instruction)
1261 || function.inputs().iter().any(|input| is_dynamic_value_type(input.value_type()))
1262 || function.outputs().iter().any(|output| is_dynamic_value_type(output.value_type()))
1263 {
1264 return Ok(true);
1265 }
1266 if let Some(finalize) = function.finalize_logic() {
1267 if finalize.inputs().iter().any(|input| matches!(input.finalize_type(), FinalizeType::DynamicFuture))
1268 || finalize.commands().iter().any(is_v14_command)
1269 {
1270 return Ok(true);
1271 }
1272 }
1273 if function.contains_identifier_type()? {
1274 return Ok(true);
1275 }
1276 }
1277
1278 for (_, closure) in self.closures() {
1280 if closure.instructions().iter().any(is_v14_instruction)
1281 || closure.inputs().iter().any(|input| is_dynamic_register_type(input.register_type()))
1282 || closure.outputs().iter().any(|output| is_dynamic_register_type(output.register_type()))
1283 {
1284 return Ok(true);
1285 }
1286 if closure.contains_identifier_type()? {
1287 return Ok(true);
1288 }
1289 }
1290
1291 if let Some(constructor) = &self.constructor {
1293 if constructor.commands().iter().any(is_v14_command) {
1294 return Ok(true);
1295 }
1296 if constructor.contains_identifier_type()? {
1297 return Ok(true);
1298 }
1299 }
1300
1301 for mapping in self.mappings.values() {
1303 if mapping.contains_identifier_type()? {
1304 return Ok(true);
1305 }
1306 }
1307 for struct_type in self.structs.values() {
1308 if struct_type.contains_identifier_type()? {
1309 return Ok(true);
1310 }
1311 }
1312 for record_type in self.records.values() {
1313 if record_type.contains_identifier_type()? {
1314 return Ok(true);
1315 }
1316 }
1317
1318 Ok(false)
1319 }
1320
1321 #[inline]
1325 pub fn contains_string_type(&self) -> bool {
1326 self.mappings.values().any(|mapping| mapping.contains_string_type())
1327 || self.structs.values().any(|struct_type| struct_type.contains_string_type())
1328 || self.records.values().any(|record_type| record_type.contains_string_type())
1329 || self.closures.values().any(|closure| closure.contains_string_type())
1330 || self.functions.values().any(|function| function.contains_string_type())
1331 || self.constructor.iter().any(|constructor| constructor.contains_string_type())
1332 }
1333}
1334
1335impl<N: Network> TypeName for ProgramCore<N> {
1336 #[inline]
1338 fn type_name() -> &'static str {
1339 "program"
1340 }
1341}
1342
1343#[cfg(test)]
1344mod tests {
1345 use super::*;
1346 use console::{
1347 network::MainnetV0,
1348 program::{Locator, ValueType},
1349 };
1350
1351 type CurrentNetwork = MainnetV0;
1352
1353 #[test]
1354 fn test_program_mapping() -> Result<()> {
1355 let mapping = Mapping::<CurrentNetwork>::from_str(
1357 r"
1358mapping message:
1359 key as field.public;
1360 value as field.public;",
1361 )?;
1362
1363 let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {mapping}"))?;
1365 assert!(program.contains_mapping(&Identifier::from_str("message")?));
1367 assert_eq!(mapping.to_string(), program.get_mapping(&Identifier::from_str("message")?)?.to_string());
1369
1370 Ok(())
1371 }
1372
1373 #[test]
1374 fn test_program_struct() -> Result<()> {
1375 let struct_ = StructType::<CurrentNetwork>::from_str(
1377 r"
1378struct message:
1379 first as field;
1380 second as field;",
1381 )?;
1382
1383 let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {struct_}"))?;
1385 assert!(program.contains_struct(&Identifier::from_str("message")?));
1387 assert_eq!(&struct_, program.get_struct(&Identifier::from_str("message")?)?);
1389
1390 Ok(())
1391 }
1392
1393 #[test]
1394 fn test_program_record() -> Result<()> {
1395 let record = RecordType::<CurrentNetwork>::from_str(
1397 r"
1398record foo:
1399 owner as address.private;
1400 first as field.private;
1401 second as field.public;",
1402 )?;
1403
1404 let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {record}"))?;
1406 assert!(program.contains_record(&Identifier::from_str("foo")?));
1408 assert_eq!(&record, program.get_record(&Identifier::from_str("foo")?)?);
1410
1411 Ok(())
1412 }
1413
1414 #[test]
1415 fn test_program_function() -> Result<()> {
1416 let function = Function::<CurrentNetwork>::from_str(
1418 r"
1419function compute:
1420 input r0 as field.public;
1421 input r1 as field.private;
1422 add r0 r1 into r2;
1423 output r2 as field.private;",
1424 )?;
1425
1426 let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {function}"))?;
1428 assert!(program.contains_function(&Identifier::from_str("compute")?));
1430 assert_eq!(function, program.get_function(&Identifier::from_str("compute")?)?);
1432
1433 Ok(())
1434 }
1435
1436 #[test]
1437 fn test_program_import() -> Result<()> {
1438 let program = Program::<CurrentNetwork>::from_str(
1440 r"
1441import eth.aleo;
1442import usdc.aleo;
1443
1444program swap.aleo;
1445
1446// The `swap` function transfers ownership of the record
1447// for token A to the record owner of token B, and vice-versa.
1448function swap:
1449 // Input the record for token A.
1450 input r0 as eth.aleo/eth.record;
1451 // Input the record for token B.
1452 input r1 as usdc.aleo/usdc.record;
1453
1454 // Send the record for token A to the owner of token B.
1455 call eth.aleo/transfer r0 r1.owner r0.amount into r2 r3;
1456
1457 // Send the record for token B to the owner of token A.
1458 call usdc.aleo/transfer r1 r0.owner r1.amount into r4 r5;
1459
1460 // Output the new record for token A.
1461 output r2 as eth.aleo/eth.record;
1462 // Output the new record for token B.
1463 output r4 as usdc.aleo/usdc.record;
1464 ",
1465 )
1466 .unwrap();
1467
1468 assert!(program.contains_import(&ProgramID::from_str("eth.aleo")?));
1470 assert!(program.contains_import(&ProgramID::from_str("usdc.aleo")?));
1471
1472 let function = program.get_function(&Identifier::from_str("swap")?)?;
1474
1475 assert_eq!(function.inputs().len(), 2);
1477 assert_eq!(function.input_types().len(), 2);
1478
1479 let expected_input_type_1 = ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?);
1481 let expected_input_type_2 = ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?);
1482
1483 assert_eq!(function.input_types()[0], expected_input_type_1);
1485 assert_eq!(function.input_types()[1], expected_input_type_2);
1486
1487 assert_eq!(function.input_types()[0].variant(), expected_input_type_1.variant());
1489 assert_eq!(function.input_types()[1].variant(), expected_input_type_2.variant());
1490
1491 assert_eq!(function.instructions().len(), 2);
1493
1494 assert_eq!(function.instructions()[0].opcode(), Opcode::Call("call"));
1496 assert_eq!(function.instructions()[1].opcode(), Opcode::Call("call"));
1497
1498 assert_eq!(function.outputs().len(), 2);
1500 assert_eq!(function.output_types().len(), 2);
1501
1502 let expected_output_type_1 = ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?);
1504 let expected_output_type_2 = ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?);
1505
1506 assert_eq!(function.output_types()[0], expected_output_type_1);
1508 assert_eq!(function.output_types()[1], expected_output_type_2);
1509
1510 assert_eq!(function.output_types()[0].variant(), expected_output_type_1.variant());
1512 assert_eq!(function.output_types()[1].variant(), expected_output_type_2.variant());
1513
1514 Ok(())
1515 }
1516
1517 #[test]
1518 fn test_program_with_constructor() {
1519 let program_string = r"import credits.aleo;
1521
1522program good_constructor.aleo;
1523
1524constructor:
1525 assert.eq edition 0u16;
1526 assert.eq credits.aleo/edition 0u16;
1527 assert.neq checksum 0field;
1528 assert.eq credits.aleo/checksum 6192738754253668739186185034243585975029374333074931926190215457304721124008field;
1529 set 1u8 into data[0u8];
1530
1531mapping data:
1532 key as u8.public;
1533 value as u8.public;
1534
1535function dummy:
1536
1537function check:
1538 async check into r0;
1539 output r0 as good_constructor.aleo/check.future;
1540
1541finalize check:
1542 get data[0u8] into r0;
1543 assert.eq r0 1u8;
1544";
1545 let program = Program::<CurrentNetwork>::from_str(program_string).unwrap();
1546
1547 let serialized = program.to_string();
1549 let deserialized = Program::<CurrentNetwork>::from_str(&serialized).unwrap();
1550 assert_eq!(program, deserialized);
1551
1552 let serialized = program.to_bytes_le().unwrap();
1553 let deserialized = Program::<CurrentNetwork>::from_bytes_le(&serialized).unwrap();
1554 assert_eq!(program, deserialized);
1555
1556 let display = format!("{program}");
1558 assert_eq!(display, program_string);
1559
1560 assert!(program.contains_constructor());
1562 assert_eq!(program.constructor().unwrap().commands().len(), 5);
1563 }
1564
1565 #[test]
1566 fn test_program_equality_and_checksum() {
1567 fn run_test(program1: &str, program2: &str, expected_equal: bool) {
1568 println!("Comparing programs:\n{program1}\n{program2}");
1569 let program1 = Program::<CurrentNetwork>::from_str(program1).unwrap();
1570 let program2 = Program::<CurrentNetwork>::from_str(program2).unwrap();
1571 assert_eq!(program1 == program2, expected_equal);
1572 assert_eq!(program1.to_checksum() == program2.to_checksum(), expected_equal);
1573 }
1574
1575 run_test(r"program test.aleo; function dummy: ", r"program test.aleo; function dummy: ", true);
1577
1578 run_test(r"program test.aleo; function dummy: ", r"program test.aleo; function bummy: ", false);
1580
1581 run_test(
1583 r"program test.aleo; function dummy: ",
1584 r"program test.aleo; constructor: assert.eq true true; function dummy: ",
1585 false,
1586 );
1587
1588 run_test(
1590 r"program test.aleo; struct foo: data as u8; function dummy:",
1591 r"program test.aleo; function dummy: struct foo: data as u8;",
1592 false,
1593 );
1594 }
1595
1596 #[test]
1597 fn test_contains_v14_syntax() -> Result<()> {
1598 let no_v14 = Program::<CurrentNetwork>::from_str(
1600 r"program test.aleo;
1601function foo:
1602 input r0 as u64.public;
1603 output r0 as u64.public;",
1604 )?;
1605 assert!(!no_v14.contains_v14_syntax()?);
1606
1607 let dynamic_record_input = Program::<CurrentNetwork>::from_str(
1609 r"program test.aleo;
1610function foo:
1611 input r0 as dynamic.record;",
1612 )?;
1613 assert!(dynamic_record_input.contains_v14_syntax()?);
1614
1615 let dynamic_future_output = Program::<CurrentNetwork>::from_str(
1617 r"program test.aleo;
1618function foo:
1619 output r0 as dynamic.future;",
1620 )?;
1621 assert!(dynamic_future_output.contains_v14_syntax()?);
1622
1623 let call_dynamic = Program::<CurrentNetwork>::from_str(
1625 r"program test.aleo;
1626function foo:
1627 input r0 as field.public;
1628 input r1 as field.public;
1629 input r2 as field.public;
1630 call.dynamic r0 r1 r2 into r3 (as u64.public);
1631 output r3 as u64.public;",
1632 )?;
1633 assert!(call_dynamic.contains_v14_syntax()?);
1634
1635 let get_record_dynamic = Program::<CurrentNetwork>::from_str(
1637 r"program test.aleo;
1638function foo:
1639 input r0 as dynamic.record;
1640 get.record.dynamic r0.amount into r1 as field;
1641 output r1 as field.public;",
1642 )?;
1643 assert!(get_record_dynamic.contains_v14_syntax()?);
1644
1645 let cast_dynamic_record = Program::<CurrentNetwork>::from_str(
1647 r"program test.aleo;
1648record token:
1649 owner as address.private;
1650 amount as u64.private;
1651function foo:
1652 input r0 as token.record;
1653 cast r0 into r1 as dynamic.record;
1654 output r0.owner as address.private;",
1655 )?;
1656 assert!(cast_dynamic_record.contains_v14_syntax()?);
1657
1658 let contains_dynamic = Program::<CurrentNetwork>::from_str(
1660 r"program test.aleo;
1661function bar:
1662 input r0 as field.public;
1663 input r1 as field.public;
1664 input r2 as field.public;
1665 input r3 as field.public;
1666 async bar r0 r1 r2 r3 into r4;
1667 output r4 as test.aleo/bar.future;
1668finalize bar:
1669 input r0 as field.public;
1670 input r1 as field.public;
1671 input r2 as field.public;
1672 input r3 as field.public;
1673 contains.dynamic r0 r1 r2[r3] into r4;",
1674 )?;
1675 assert!(contains_dynamic.contains_v14_syntax()?);
1676
1677 let get_dynamic = Program::<CurrentNetwork>::from_str(
1679 r"program test.aleo;
1680function bar:
1681 input r0 as field.public;
1682 input r1 as field.public;
1683 input r2 as field.public;
1684 input r3 as field.public;
1685 async bar r0 r1 r2 r3 into r4;
1686 output r4 as test.aleo/bar.future;
1687finalize bar:
1688 input r0 as field.public;
1689 input r1 as field.public;
1690 input r2 as field.public;
1691 input r3 as field.public;
1692 get.dynamic r0 r1 r2[r3] into r4 as field;",
1693 )?;
1694 assert!(get_dynamic.contains_v14_syntax()?);
1695
1696 let dynamic_future_finalize_input = Program::<CurrentNetwork>::from_str(
1698 r"program test.aleo;
1699function foo:
1700 input r0 as field.public;
1701 async foo r0 into r1;
1702 output r1 as test.aleo/foo.future;
1703finalize foo:
1704 input r0 as dynamic.future;
1705 await r0;",
1706 )?;
1707 assert!(dynamic_future_finalize_input.contains_v14_syntax()?);
1708
1709 let closure_dynamic_input = Program::<CurrentNetwork>::from_str(
1711 r"program test.aleo;
1712closure bar:
1713 input r0 as dynamic.record;
1714 input r1 as field;
1715 add r1 r1 into r2;",
1716 )?;
1717 assert!(closure_dynamic_input.contains_v14_syntax()?);
1718
1719 let closure_dynamic_output = Program::<CurrentNetwork>::from_str(
1721 r"program test.aleo;
1722closure bar:
1723 input r0 as field;
1724 add r0 r0 into r1;
1725 output r1 as dynamic.record;",
1726 )?;
1727 assert!(closure_dynamic_output.contains_v14_syntax()?);
1728
1729 let get_or_use_dynamic = Program::<CurrentNetwork>::from_str(
1731 r"program test.aleo;
1732function bar:
1733 input r0 as field.public;
1734 input r1 as field.public;
1735 input r2 as field.public;
1736 input r3 as field.public;
1737 input r4 as u64.public;
1738 async bar r0 r1 r2 r3 r4 into r5;
1739 output r5 as test.aleo/bar.future;
1740finalize bar:
1741 input r0 as field.public;
1742 input r1 as field.public;
1743 input r2 as field.public;
1744 input r3 as field.public;
1745 input r4 as u64.public;
1746 get.or_use.dynamic r0 r1 r2[r3] r4 into r5 as u64;",
1747 )?;
1748 assert!(get_or_use_dynamic.contains_v14_syntax()?);
1749
1750 let snark_verify = Program::<CurrentNetwork>::from_str(
1752 r"program test.aleo;
1753function foo:
1754 input r0 as [u8; 8u32].public;
1755 input r1 as [field; 1u32].public;
1756 input r2 as [u8; 8u32].public;
1757 async foo r0 r1 r2 into r3;
1758 output r3 as test.aleo/foo.future;
1759finalize foo:
1760 input r0 as [u8; 8u32].public;
1761 input r1 as [field; 1u32].public;
1762 input r2 as [u8; 8u32].public;
1763 snark.verify r0 1u8 r1 r2 into r3;",
1764 )?;
1765 assert!(snark_verify.contains_v14_syntax()?);
1766
1767 let snark_verify_batch = Program::<CurrentNetwork>::from_str(
1769 r"program test.aleo;
1770function foo:
1771 input r0 as [[u8; 8u32]; 1u32].public;
1772 input r1 as [[[field; 1u32]; 1u32]; 1u32].public;
1773 input r2 as [u8; 8u32].public;
1774 async foo r0 r1 r2 into r3;
1775 output r3 as test.aleo/foo.future;
1776finalize foo:
1777 input r0 as [[u8; 8u32]; 1u32].public;
1778 input r1 as [[[field; 1u32]; 1u32]; 1u32].public;
1779 input r2 as [u8; 8u32].public;
1780 snark.verify.batch r0 1u8 r1 r2 into r3;",
1781 )?;
1782 assert!(snark_verify_batch.contains_v14_syntax()?);
1783
1784 let aleo_generator = Program::<CurrentNetwork>::from_str(
1786 r"program test.aleo;
1787function foo:
1788 input r0 as scalar.public;
1789 mul aleo::GENERATOR r0 into r1;
1790 output r1 as group.public;",
1791 )?;
1792 assert!(aleo_generator.contains_v14_syntax()?);
1793
1794 let aleo_generator_powers = Program::<CurrentNetwork>::from_str(
1796 r"program test.aleo;
1797function foo:
1798 input r0 as scalar.public;
1799 mul aleo::GENERATOR_POWERS[0u32] r0 into r1;
1800 output r1 as group.public;",
1801 )?;
1802 assert!(aleo_generator_powers.contains_v14_syntax()?);
1803
1804 let closure_dynamic_future_output = Program::<CurrentNetwork>::from_str(
1806 r"program test.aleo;
1807closure bar:
1808 input r0 as field;
1809 add r0 r0 into r1;
1810 output r1 as dynamic.future;",
1811 )?;
1812 assert!(closure_dynamic_future_output.contains_v14_syntax()?);
1813
1814 let constructor_v14 = Program::<CurrentNetwork>::from_str(
1816 r"program test.aleo;
1817function dummy:
1818 input r0 as field.public;
1819 output r0 as field.public;
1820constructor:
1821 assert.eq aleo::GENERATOR aleo::GENERATOR;",
1822 )?;
1823 assert!(constructor_v14.contains_v14_syntax()?);
1824
1825 Ok(())
1826 }
1827}