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 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 Constructor,
132 Identifier(Identifier<N>),
134}
135
136#[cfg(not(feature = "serial"))]
137use rayon::prelude::*;
138
139#[derive(Copy, Clone, PartialEq, Eq, Hash)]
140enum ProgramDefinition {
141 Constructor,
143 Mapping,
145 Struct,
147 Record,
149 Closure,
151 Function,
153 View,
155}
156
157#[derive(Clone)]
158pub struct ProgramCore<N: Network> {
159 id: ProgramID<N>,
161 imports: IndexMap<ProgramID<N>, Import<N>>,
163 components: IndexMap<ProgramLabel<N>, ProgramDefinition>,
165 constructor: Option<ConstructorCore<N>>,
167 mappings: IndexMap<Identifier<N>, Mapping<N>>,
169 structs: IndexMap<Identifier<N>, StructType<N>>,
171 records: IndexMap<Identifier<N>, RecordType<N>>,
173 closures: IndexMap<Identifier<N>, ClosureCore<N>>,
175 functions: IndexMap<Identifier<N>, FunctionCore<N>>,
177 views: IndexMap<Identifier<N>, ViewCore<N>>,
179}
180
181impl<N: Network> PartialEq for ProgramCore<N> {
182 fn eq(&self, other: &Self) -> bool {
185 if self.components.len() != other.components.len() {
187 return false;
188 }
189 for (left, right) in self.components.iter().zip_eq(other.components.iter()) {
191 if left != right {
192 return false;
193 }
194 }
195 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 #[rustfmt::skip]
214 pub const KEYWORDS: &'static [&'static str] = &[
215 "const",
217 "constant",
218 "public",
219 "private",
220 "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 "true",
240 "false",
241 "input",
243 "output",
244 "as",
245 "into",
246 "record",
248 "owner",
249 "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 "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 #[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 #[inline]
301 pub fn new(id: ProgramID<N>) -> Result<Self> {
302 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 #[inline]
321 pub fn credits() -> Result<Self> {
322 Self::from_str(include_str!("./resources/credits.aleo"))
323 }
324
325 pub const fn id(&self) -> &ProgramID<N> {
327 &self.id
328 }
329
330 pub const fn imports(&self) -> &IndexMap<ProgramID<N>, Import<N>> {
332 &self.imports
333 }
334
335 pub const fn constructor(&self) -> Option<&ConstructorCore<N>> {
337 self.constructor.as_ref()
338 }
339
340 pub const fn mappings(&self) -> &IndexMap<Identifier<N>, Mapping<N>> {
342 &self.mappings
343 }
344
345 pub const fn structs(&self) -> &IndexMap<Identifier<N>, StructType<N>> {
347 &self.structs
348 }
349
350 pub const fn records(&self) -> &IndexMap<Identifier<N>, RecordType<N>> {
352 &self.records
353 }
354
355 pub const fn closures(&self) -> &IndexMap<Identifier<N>, ClosureCore<N>> {
357 &self.closures
358 }
359
360 pub const fn functions(&self) -> &IndexMap<Identifier<N>, FunctionCore<N>> {
362 &self.functions
363 }
364
365 pub const fn views(&self) -> &IndexMap<Identifier<N>, ViewCore<N>> {
367 &self.views
368 }
369
370 pub fn contains_import(&self, id: &ProgramID<N>) -> bool {
372 self.imports.contains_key(id)
373 }
374
375 pub const fn contains_constructor(&self) -> bool {
377 self.constructor.is_some()
378 }
379
380 pub fn contains_mapping(&self, name: &Identifier<N>) -> bool {
382 self.mappings.contains_key(name)
383 }
384
385 pub fn contains_struct(&self, name: &Identifier<N>) -> bool {
387 self.structs.contains_key(name)
388 }
389
390 pub fn contains_record(&self, name: &Identifier<N>) -> bool {
392 self.records.contains_key(name)
393 }
394
395 pub fn contains_closure(&self, name: &Identifier<N>) -> bool {
397 self.closures.contains_key(name)
398 }
399
400 pub fn contains_function(&self, name: &Identifier<N>) -> bool {
402 self.functions.contains_key(name)
403 }
404
405 pub fn contains_view(&self, name: &Identifier<N>) -> bool {
407 self.views.contains_key(name)
408 }
409
410 pub fn get_mapping(&self, name: &Identifier<N>) -> Result<Mapping<N>> {
412 let mapping = self.mappings.get(name).cloned().ok_or_else(|| anyhow!("Mapping '{name}' is not defined."))?;
414 ensure!(mapping.name() == name, "Expected mapping '{name}', but found mapping '{}'", mapping.name());
416 Ok(mapping)
418 }
419
420 pub fn get_struct(&self, name: &Identifier<N>) -> Result<&StructType<N>> {
422 let struct_ = self.structs.get(name).ok_or_else(|| anyhow!("Struct '{name}' is not defined."))?;
424 ensure!(struct_.name() == name, "Expected struct '{name}', but found struct '{}'", struct_.name());
426 ensure!(!struct_.members().is_empty(), "Struct '{name}' is missing members.");
428 Ok(struct_)
430 }
431
432 pub fn get_record(&self, name: &Identifier<N>) -> Result<&RecordType<N>> {
434 let record = self.records.get(name).ok_or_else(|| anyhow!("Record '{name}' is not defined."))?;
436 ensure!(record.name() == name, "Expected record '{name}', but found record '{}'", record.name());
438 Ok(record)
440 }
441
442 pub fn get_closure(&self, name: &Identifier<N>) -> Result<ClosureCore<N>> {
444 self.get_closure_ref(name).cloned()
445 }
446
447 pub fn get_closure_ref(&self, name: &Identifier<N>) -> Result<&ClosureCore<N>> {
449 let closure = self.closures.get(name).ok_or_else(|| anyhow!("Closure '{name}' is not defined."))?;
451 ensure!(closure.name() == name, "Expected closure '{name}', but found closure '{}'", closure.name());
453 ensure!(!closure.inputs().is_empty(), "Cannot evaluate a closure without input statements");
455 ensure!(closure.inputs().len() <= N::MAX_INPUTS, "Closure exceeds maximum number of inputs");
457 ensure!(!closure.instructions().is_empty(), "Cannot evaluate a closure without instructions");
459 ensure!(closure.outputs().len() <= N::MAX_OUTPUTS, "Closure exceeds maximum number of outputs");
461 Ok(closure)
463 }
464
465 pub fn get_function(&self, name: &Identifier<N>) -> Result<FunctionCore<N>> {
467 self.get_function_ref(name).cloned()
468 }
469
470 pub fn get_function_ref(&self, name: &Identifier<N>) -> Result<&FunctionCore<N>> {
472 let function = self.functions.get(name).ok_or(anyhow!("Function '{}/{name}' is not defined.", self.id))?;
474 ensure!(function.name() == name, "Expected function '{name}', but found function '{}'", function.name());
476 ensure!(function.inputs().len() <= N::MAX_INPUTS, "Function exceeds maximum number of inputs");
478 ensure!(function.instructions().len() <= N::MAX_INSTRUCTIONS, "Function exceeds maximum instructions");
480 ensure!(function.outputs().len() <= N::MAX_OUTPUTS, "Function exceeds maximum number of outputs");
482 Ok(function)
484 }
485
486 pub fn get_view(&self, name: &Identifier<N>) -> Result<ViewCore<N>> {
488 self.get_view_ref(name).cloned()
489 }
490
491 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 #[inline]
506 fn add_import(&mut self, import: Import<N>) -> Result<()> {
507 let import_name = *import.name();
509
510 ensure!(self.imports.len() < N::MAX_IMPORTS, "Program exceeds the maximum number of imports");
512
513 ensure!(self.is_unique_name(&import_name), "'{import_name}' is already in use.");
515 ensure!(!Self::is_reserved_opcode(&import_name.to_string()), "'{import_name}' is a reserved opcode.");
517 ensure!(!Self::is_reserved_keyword(&import_name), "'{import_name}' is a reserved keyword.");
519
520 ensure!(
522 !self.imports.contains_key(import.program_id()),
523 "Import '{}' is already defined.",
524 import.program_id()
525 );
526
527 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 fn add_constructor(&mut self, constructor: ConstructorCore<N>) -> Result<()> {
540 ensure!(self.constructor.is_none(), "Program already has a constructor.");
542 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 if self.components.insert(ProgramLabel::Constructor, ProgramDefinition::Constructor).is_some() {
547 bail!("Constructor already exists in the program.")
548 }
549 self.constructor = Some(constructor);
551 Ok(())
552 }
553
554 #[inline]
560 fn add_mapping(&mut self, mapping: Mapping<N>) -> Result<()> {
561 let mapping_name = *mapping.name();
563
564 ensure!(self.mappings.len() < N::LATEST_MAX_MAPPINGS(), "Program exceeds the maximum number of mappings");
566
567 ensure!(self.is_unique_name(&mapping_name), "'{mapping_name}' is already in use.");
569 ensure!(!Self::is_reserved_keyword(&mapping_name), "'{mapping_name}' is a reserved keyword.");
571 ensure!(!Self::is_reserved_opcode(&mapping_name.to_string()), "'{mapping_name}' is a reserved opcode.");
573
574 if self.components.insert(ProgramLabel::Identifier(mapping_name), ProgramDefinition::Mapping).is_some() {
576 bail!("'{mapping_name}' already exists in the program.")
577 }
578 if self.mappings.insert(mapping_name, mapping).is_some() {
580 bail!("'{mapping_name}' already exists in the program.")
581 }
582 Ok(())
583 }
584
585 #[inline]
593 fn add_struct(&mut self, struct_: StructType<N>) -> Result<()> {
594 let struct_name = *struct_.name();
596
597 ensure!(self.structs.len() < N::MAX_STRUCTS, "Program exceeds the maximum number of structs.");
599
600 ensure!(self.is_unique_name(&struct_name), "'{struct_name}' is already in use.");
602 ensure!(!Self::is_reserved_opcode(&struct_name.to_string()), "'{struct_name}' is a reserved opcode.");
604 ensure!(!Self::is_reserved_keyword(&struct_name), "'{struct_name}' is a reserved keyword.");
606
607 ensure!(!struct_.members().is_empty(), "Struct '{struct_name}' is missing members.");
609
610 for (identifier, plaintext_type) in struct_.members() {
613 ensure!(!Self::is_reserved_keyword(identifier), "'{identifier}' is a reserved keyword.");
615 match plaintext_type {
617 PlaintextType::Literal(_) => continue,
618 PlaintextType::Struct(member_identifier) => {
619 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 {
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 if self.components.insert(ProgramLabel::Identifier(struct_name), ProgramDefinition::Struct).is_some() {
657 bail!("'{struct_name}' already exists in the program.")
658 }
659 if self.structs.insert(struct_name, struct_).is_some() {
661 bail!("'{struct_name}' already exists in the program.")
662 }
663 Ok(())
664 }
665
666 #[inline]
674 fn add_record(&mut self, record: RecordType<N>) -> Result<()> {
675 let record_name = *record.name();
677
678 ensure!(self.records.len() < N::MAX_RECORDS, "Program exceeds the maximum number of records.");
680
681 ensure!(self.is_unique_name(&record_name), "'{record_name}' is already in use.");
683 ensure!(!Self::is_reserved_opcode(&record_name.to_string()), "'{record_name}' is a reserved opcode.");
685 ensure!(!Self::is_reserved_keyword(&record_name), "'{record_name}' is a reserved keyword.");
687
688 for (identifier, entry_type) in record.entries() {
691 ensure!(!Self::is_reserved_keyword(identifier), "'{identifier}' is a reserved keyword.");
693 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 {
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 if self.components.insert(ProgramLabel::Identifier(record_name), ProgramDefinition::Record).is_some() {
734 bail!("'{record_name}' already exists in the program.")
735 }
736 if self.records.insert(record_name, record).is_some() {
738 bail!("'{record_name}' already exists in the program.")
739 }
740 Ok(())
741 }
742
743 #[inline]
757 fn add_closure(&mut self, closure: ClosureCore<N>) -> Result<()> {
758 let closure_name = *closure.name();
760
761 ensure!(self.closures.len() < N::MAX_CLOSURES, "Program exceeds the maximum number of closures.");
763
764 ensure!(self.is_unique_name(&closure_name), "'{closure_name}' is already in use.");
766 ensure!(!Self::is_reserved_opcode(&closure_name.to_string()), "'{closure_name}' is a reserved opcode.");
768 ensure!(!Self::is_reserved_keyword(&closure_name), "'{closure_name}' is a reserved keyword.");
770
771 ensure!(!closure.inputs().is_empty(), "Cannot evaluate a closure without input statements");
773 ensure!(closure.inputs().len() <= N::MAX_INPUTS, "Closure exceeds maximum number of inputs");
775 ensure!(!closure.instructions().is_empty(), "Cannot evaluate a closure without instructions");
777 ensure!(closure.outputs().len() <= N::MAX_OUTPUTS, "Closure exceeds maximum number of outputs");
779
780 if self.components.insert(ProgramLabel::Identifier(closure_name), ProgramDefinition::Closure).is_some() {
782 bail!("'{closure_name}' already exists in the program.")
783 }
784 if self.closures.insert(closure_name, closure).is_some() {
786 bail!("'{closure_name}' already exists in the program.")
787 }
788 Ok(())
789 }
790
791 #[inline]
805 fn add_function(&mut self, function: FunctionCore<N>) -> Result<()> {
806 let function_name = *function.name();
808
809 ensure!(self.functions.len() < N::MAX_FUNCTIONS, "Program exceeds the maximum number of functions");
811
812 ensure!(self.is_unique_name(&function_name), "'{function_name}' is already in use.");
814 ensure!(!Self::is_reserved_opcode(&function_name.to_string()), "'{function_name}' is a reserved opcode.");
816 ensure!(!Self::is_reserved_keyword(&function_name), "'{function_name}' is a reserved keyword.");
818
819 ensure!(function.inputs().len() <= N::MAX_INPUTS, "Function exceeds maximum number of inputs");
821 ensure!(function.instructions().len() <= N::MAX_INSTRUCTIONS, "Function exceeds maximum instructions");
823 ensure!(function.outputs().len() <= N::MAX_OUTPUTS, "Function exceeds maximum number of outputs");
825
826 if self.components.insert(ProgramLabel::Identifier(function_name), ProgramDefinition::Function).is_some() {
828 bail!("'{function_name}' already exists in the program.")
829 }
830 if self.functions.insert(function_name, function).is_some() {
832 bail!("'{function_name}' already exists in the program.")
833 }
834 Ok(())
835 }
836
837 #[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 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 fn is_unique_name(&self, name: &Identifier<N>) -> bool {
869 !self.components.contains_key(&ProgramLabel::Identifier(*name))
870 }
871
872 pub fn is_reserved_opcode(name: &str) -> bool {
874 Instruction::<N>::is_reserved_opcode(name)
875 }
876
877 pub fn is_reserved_keyword(name: &Identifier<N>) -> bool {
879 let name = name.to_string();
881 Self::KEYWORDS.iter().any(|keyword| *keyword == name)
883 }
884
885 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 pub fn check_restricted_keywords_for_consensus_version(&self, consensus_version: ConsensusVersion) -> Result<()> {
900 let keywords =
902 Program::<N>::restricted_keywords_for_consensus_version(consensus_version).collect::<IndexSet<_>>();
903 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 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 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 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 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 pub fn check_program_naming_structure(&self) -> Result<()> {
960 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 let record_names: BTreeSet<String> = self.records.keys().map(|name| name.to_string()).collect();
968
969 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 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 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 pub fn check_external_calls_to_credits_upgrade(&self) -> Result<()> {
1000 cfg_iter!(self.functions()).flat_map(|(_, function)| function.instructions()).try_for_each(|instruction| {
1002 if let Some(CallOperator::Locator(locator)) = instruction.call_operator() {
1003 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 #[inline]
1017 pub fn contains_v9_syntax(&self) -> bool {
1018 if self.contains_constructor() {
1020 return true;
1021 }
1022 for function in self.functions().values() {
1025 if let Some(finalize_logic) = function.finalize_logic() {
1027 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 false
1039 }
1040
1041 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 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 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 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 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 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 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 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 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 #[inline]
1191 pub fn contains_v11_syntax(&self) -> bool {
1192 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 let function_contains = cfg_iter!(self.functions())
1205 .flat_map(|(_, function)| function.instructions())
1206 .any(|instruction| has_op(*instruction.opcode()));
1207
1208 let closure_contains = cfg_iter!(self.closures())
1210 .flat_map(|(_, closure)| closure.instructions())
1211 .any(|instruction| has_op(*instruction.opcode()));
1212
1213 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 #[inline]
1227 pub fn contains_v12_syntax(&self) -> bool {
1228 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 pub fn check_program_size(&self, block_height: u32) -> Result<()> {
1241 let program_size = self.to_string().len();
1243 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 pub fn check_program_writes(&self, block_height: u32) -> Result<()> {
1258 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 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 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 #[inline]
1302 pub fn contains_v14_syntax(&self) -> Result<bool> {
1303 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 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 let is_dynamic_value_type =
1326 |vt: &ValueType<N>| matches!(vt, ValueType::DynamicRecord | ValueType::DynamicFuture);
1327
1328 let is_dynamic_register_type =
1330 |rt: &RegisterType<N>| matches!(rt, RegisterType::DynamicRecord | RegisterType::DynamicFuture);
1331
1332 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 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 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 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 #[inline]
1402 pub fn contains_v15_syntax(&self) -> bool {
1403 let has_op = |opcode: &str| opcode.starts_with("commit.") && opcode.ends_with(".raw");
1405
1406 let function_contains = cfg_iter!(self.functions())
1408 .flat_map(|(_, function)| function.instructions())
1409 .any(|instruction| has_op(*instruction.opcode()));
1410
1411 let closure_contains = cfg_iter!(self.closures())
1413 .flat_map(|(_, closure)| closure.instructions())
1414 .any(|instruction| has_op(*instruction.opcode()));
1415
1416 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 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 #[inline]
1443 pub fn contains_v16_syntax(&self) -> bool {
1444 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 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 #[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 #[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 let mapping = Mapping::<CurrentNetwork>::from_str(
1499 r"
1500mapping message:
1501 key as field.public;
1502 value as field.public;",
1503 )?;
1504
1505 let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {mapping}"))?;
1507 assert!(program.contains_mapping(&Identifier::from_str("message")?));
1509 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 let struct_ = StructType::<CurrentNetwork>::from_str(
1519 r"
1520struct message:
1521 first as field;
1522 second as field;",
1523 )?;
1524
1525 let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {struct_}"))?;
1527 assert!(program.contains_struct(&Identifier::from_str("message")?));
1529 assert_eq!(&struct_, program.get_struct(&Identifier::from_str("message")?)?);
1531
1532 Ok(())
1533 }
1534
1535 #[test]
1536 fn test_program_record() -> Result<()> {
1537 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 let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {record}"))?;
1548 assert!(program.contains_record(&Identifier::from_str("foo")?));
1550 assert_eq!(&record, program.get_record(&Identifier::from_str("foo")?)?);
1552
1553 Ok(())
1554 }
1555
1556 #[test]
1557 fn test_program_function() -> Result<()> {
1558 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 let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {function}"))?;
1570 assert!(program.contains_function(&Identifier::from_str("compute")?));
1572 assert_eq!(function, program.get_function(&Identifier::from_str("compute")?)?);
1574
1575 Ok(())
1576 }
1577
1578 #[test]
1579 fn test_program_import() -> Result<()> {
1580 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 assert!(program.contains_import(&ProgramID::from_str("eth.aleo")?));
1612 assert!(program.contains_import(&ProgramID::from_str("usdc.aleo")?));
1613
1614 let function = program.get_function(&Identifier::from_str("swap")?)?;
1616
1617 assert_eq!(function.inputs().len(), 2);
1619 assert_eq!(function.input_types().len(), 2);
1620
1621 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 assert_eq!(function.input_types()[0], expected_input_type_1);
1627 assert_eq!(function.input_types()[1], expected_input_type_2);
1628
1629 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 assert_eq!(function.instructions().len(), 2);
1635
1636 assert_eq!(function.instructions()[0].opcode(), Opcode::Call("call"));
1638 assert_eq!(function.instructions()[1].opcode(), Opcode::Call("call"));
1639
1640 assert_eq!(function.outputs().len(), 2);
1642 assert_eq!(function.output_types().len(), 2);
1643
1644 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 assert_eq!(function.output_types()[0], expected_output_type_1);
1650 assert_eq!(function.output_types()[1], expected_output_type_2);
1651
1652 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 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 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 let display = format!("{program}");
1700 assert_eq!(display, program_string);
1701
1702 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 run_test(r"program test.aleo; function dummy: ", r"program test.aleo; function dummy: ", true);
1719
1720 run_test(r"program test.aleo; function dummy: ", r"program test.aleo; function bummy: ", false);
1722
1723 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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}