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::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 if record_name.starts_with(previous) {
982 bail!("Record name '{previous}' can't be a prefix of record name '{record_name}'.");
983 }
984 }
985 previous_record_name = Some(record_name);
986 }
987
988 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 if finalize.inputs().iter().any(|input| matches!(input.finalize_type(), FinalizeType::DynamicFuture))
1342 || finalize.commands().iter().any(is_v14_command)
1343 {
1344 return Ok(true);
1345 }
1346 }
1347 if function.contains_identifier_type()? {
1348 return Ok(true);
1349 }
1350 }
1351
1352 for (_, closure) in self.closures() {
1354 if closure.instructions().iter().any(is_v14_instruction)
1355 || closure.inputs().iter().any(|input| is_dynamic_register_type(input.register_type()))
1356 || closure.outputs().iter().any(|output| is_dynamic_register_type(output.register_type()))
1357 {
1358 return Ok(true);
1359 }
1360 if closure.contains_identifier_type()? {
1361 return Ok(true);
1362 }
1363 }
1364
1365 if let Some(constructor) = &self.constructor {
1367 if constructor.commands().iter().any(is_v14_command) {
1368 return Ok(true);
1369 }
1370 if constructor.contains_identifier_type()? {
1371 return Ok(true);
1372 }
1373 }
1374
1375 for mapping in self.mappings.values() {
1377 if mapping.contains_identifier_type()? {
1378 return Ok(true);
1379 }
1380 }
1381 for struct_type in self.structs.values() {
1382 if struct_type.contains_identifier_type()? {
1383 return Ok(true);
1384 }
1385 }
1386 for record_type in self.records.values() {
1387 if record_type.contains_identifier_type()? {
1388 return Ok(true);
1389 }
1390 }
1391
1392 Ok(false)
1393 }
1394
1395 #[inline]
1403 pub fn contains_v15_syntax(&self) -> bool {
1404 let has_op = |opcode: &str| opcode.starts_with("commit.") && opcode.ends_with(".raw");
1406
1407 let function_contains = cfg_iter!(self.functions())
1409 .flat_map(|(_, function)| function.instructions())
1410 .any(|instruction| has_op(*instruction.opcode()));
1411
1412 let closure_contains = cfg_iter!(self.closures())
1414 .flat_map(|(_, closure)| closure.instructions())
1415 .any(|instruction| has_op(*instruction.opcode()));
1416
1417 let command_contains = cfg_iter!(self.functions())
1419 .flat_map(|(_, function)| function.finalize_logic().map(|finalize| finalize.commands()))
1420 .flatten()
1421 .chain(cfg_iter!(self.constructor).flat_map(|constructor| constructor.commands()))
1422 .any(|command| matches!(command, Command::Instruction(instruction) if has_op(*instruction.opcode())));
1423
1424 let finalize_has_call = cfg_iter!(self.functions())
1433 .filter_map(|(_, function)| function.finalize_logic())
1434 .flat_map(|finalize| finalize.commands())
1435 .any(|command| matches!(command, Command::Instruction(Instruction::Call(_))));
1436
1437 function_contains || closure_contains || command_contains || finalize_has_call || !self.views.is_empty()
1438 }
1439
1440 #[inline]
1444 pub fn contains_v16_syntax(&self) -> bool {
1445 let command_contains = cfg_iter!(self.functions())
1447 .flat_map(|(_, function)| function.finalize_logic().map(|finalize| finalize.commands()))
1448 .flatten()
1449 .chain(cfg_iter!(self.constructor).flat_map(|constructor| constructor.commands()))
1450 .chain(cfg_iter!(self.views).flat_map(|(_, view)| view.commands()))
1451 .flat_map(|command| command.operands())
1452 .any(|operand| matches!(operand, Operand::ComponentChecksum(..)));
1453
1454 let view_output_contains = self
1456 .views
1457 .values()
1458 .any(|view| view.outputs().iter().any(|output| matches!(output.operand(), Operand::ComponentChecksum(..))));
1459
1460 command_contains || view_output_contains
1461 }
1462
1463 #[inline]
1467 pub fn contains_string_type(&self) -> bool {
1468 self.mappings.values().any(|mapping| mapping.contains_string_type())
1469 || self.structs.values().any(|struct_type| struct_type.contains_string_type())
1470 || self.records.values().any(|record_type| record_type.contains_string_type())
1471 || self.closures.values().any(|closure| closure.contains_string_type())
1472 || self.functions.values().any(|function| function.contains_string_type())
1473 || self.constructor.iter().any(|constructor| constructor.contains_string_type())
1474 || self.views.values().any(|view| view.contains_string_type())
1475 }
1476}
1477
1478impl<N: Network> TypeName for ProgramCore<N> {
1479 #[inline]
1481 fn type_name() -> &'static str {
1482 "program"
1483 }
1484}
1485
1486#[cfg(test)]
1487mod tests {
1488 use super::*;
1489 use console::{
1490 network::MainnetV0,
1491 program::{Locator, ValueType},
1492 };
1493
1494 type CurrentNetwork = MainnetV0;
1495
1496 #[test]
1497 fn test_program_mapping() -> Result<()> {
1498 let mapping = Mapping::<CurrentNetwork>::from_str(
1500 r"
1501mapping message:
1502 key as field.public;
1503 value as field.public;",
1504 )?;
1505
1506 let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {mapping}"))?;
1508 assert!(program.contains_mapping(&Identifier::from_str("message")?));
1510 assert_eq!(mapping.to_string(), program.get_mapping(&Identifier::from_str("message")?)?.to_string());
1512
1513 Ok(())
1514 }
1515
1516 #[test]
1517 fn test_program_struct() -> Result<()> {
1518 let struct_ = StructType::<CurrentNetwork>::from_str(
1520 r"
1521struct message:
1522 first as field;
1523 second as field;",
1524 )?;
1525
1526 let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {struct_}"))?;
1528 assert!(program.contains_struct(&Identifier::from_str("message")?));
1530 assert_eq!(&struct_, program.get_struct(&Identifier::from_str("message")?)?);
1532
1533 Ok(())
1534 }
1535
1536 #[test]
1537 fn test_program_record() -> Result<()> {
1538 let record = RecordType::<CurrentNetwork>::from_str(
1540 r"
1541record foo:
1542 owner as address.private;
1543 first as field.private;
1544 second as field.public;",
1545 )?;
1546
1547 let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {record}"))?;
1549 assert!(program.contains_record(&Identifier::from_str("foo")?));
1551 assert_eq!(&record, program.get_record(&Identifier::from_str("foo")?)?);
1553
1554 Ok(())
1555 }
1556
1557 #[test]
1558 fn test_program_function() -> Result<()> {
1559 let function = Function::<CurrentNetwork>::from_str(
1561 r"
1562function compute:
1563 input r0 as field.public;
1564 input r1 as field.private;
1565 add r0 r1 into r2;
1566 output r2 as field.private;",
1567 )?;
1568
1569 let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {function}"))?;
1571 assert!(program.contains_function(&Identifier::from_str("compute")?));
1573 assert_eq!(function, program.get_function(&Identifier::from_str("compute")?)?);
1575
1576 Ok(())
1577 }
1578
1579 #[test]
1580 fn test_program_import() -> Result<()> {
1581 let program = Program::<CurrentNetwork>::from_str(
1583 r"
1584import eth.aleo;
1585import usdc.aleo;
1586
1587program swap.aleo;
1588
1589// The `swap` function transfers ownership of the record
1590// for token A to the record owner of token B, and vice-versa.
1591function swap:
1592 // Input the record for token A.
1593 input r0 as eth.aleo/eth.record;
1594 // Input the record for token B.
1595 input r1 as usdc.aleo/usdc.record;
1596
1597 // Send the record for token A to the owner of token B.
1598 call eth.aleo/transfer r0 r1.owner r0.amount into r2 r3;
1599
1600 // Send the record for token B to the owner of token A.
1601 call usdc.aleo/transfer r1 r0.owner r1.amount into r4 r5;
1602
1603 // Output the new record for token A.
1604 output r2 as eth.aleo/eth.record;
1605 // Output the new record for token B.
1606 output r4 as usdc.aleo/usdc.record;
1607 ",
1608 )
1609 .unwrap();
1610
1611 assert!(program.contains_import(&ProgramID::from_str("eth.aleo")?));
1613 assert!(program.contains_import(&ProgramID::from_str("usdc.aleo")?));
1614
1615 let function = program.get_function(&Identifier::from_str("swap")?)?;
1617
1618 assert_eq!(function.inputs().len(), 2);
1620 assert_eq!(function.input_types().len(), 2);
1621
1622 let expected_input_type_1 = ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?);
1624 let expected_input_type_2 = ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?);
1625
1626 assert_eq!(function.input_types()[0], expected_input_type_1);
1628 assert_eq!(function.input_types()[1], expected_input_type_2);
1629
1630 assert_eq!(function.input_types()[0].variant(), expected_input_type_1.variant());
1632 assert_eq!(function.input_types()[1].variant(), expected_input_type_2.variant());
1633
1634 assert_eq!(function.instructions().len(), 2);
1636
1637 assert_eq!(function.instructions()[0].opcode(), Opcode::Call("call"));
1639 assert_eq!(function.instructions()[1].opcode(), Opcode::Call("call"));
1640
1641 assert_eq!(function.outputs().len(), 2);
1643 assert_eq!(function.output_types().len(), 2);
1644
1645 let expected_output_type_1 = ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?);
1647 let expected_output_type_2 = ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?);
1648
1649 assert_eq!(function.output_types()[0], expected_output_type_1);
1651 assert_eq!(function.output_types()[1], expected_output_type_2);
1652
1653 assert_eq!(function.output_types()[0].variant(), expected_output_type_1.variant());
1655 assert_eq!(function.output_types()[1].variant(), expected_output_type_2.variant());
1656
1657 Ok(())
1658 }
1659
1660 #[test]
1661 fn test_program_with_constructor() {
1662 let program_string = r"import credits.aleo;
1664
1665program good_constructor.aleo;
1666
1667constructor:
1668 assert.eq edition 0u16;
1669 assert.eq credits.aleo/edition 0u16;
1670 assert.neq checksum 0field;
1671 assert.eq credits.aleo/checksum 6192738754253668739186185034243585975029374333074931926190215457304721124008field;
1672 set 1u8 into data[0u8];
1673
1674mapping data:
1675 key as u8.public;
1676 value as u8.public;
1677
1678function dummy:
1679
1680function check:
1681 async check into r0;
1682 output r0 as good_constructor.aleo/check.future;
1683
1684finalize check:
1685 get data[0u8] into r0;
1686 assert.eq r0 1u8;
1687";
1688 let program = Program::<CurrentNetwork>::from_str(program_string).unwrap();
1689
1690 let serialized = program.to_string();
1692 let deserialized = Program::<CurrentNetwork>::from_str(&serialized).unwrap();
1693 assert_eq!(program, deserialized);
1694
1695 let serialized = program.to_bytes_le().unwrap();
1696 let deserialized = Program::<CurrentNetwork>::from_bytes_le(&serialized).unwrap();
1697 assert_eq!(program, deserialized);
1698
1699 let display = format!("{program}");
1701 assert_eq!(display, program_string);
1702
1703 assert!(program.contains_constructor());
1705 assert_eq!(program.constructor().unwrap().commands().len(), 5);
1706 }
1707
1708 #[test]
1709 fn test_program_equality_and_checksum() {
1710 fn run_test(program1: &str, program2: &str, expected_equal: bool) {
1711 println!("Comparing programs:\n{program1}\n{program2}");
1712 let program1 = Program::<CurrentNetwork>::from_str(program1).unwrap();
1713 let program2 = Program::<CurrentNetwork>::from_str(program2).unwrap();
1714 assert_eq!(program1 == program2, expected_equal);
1715 assert_eq!(program1.to_checksum() == program2.to_checksum(), expected_equal);
1716 }
1717
1718 run_test(r"program test.aleo; function dummy: ", r"program test.aleo; function dummy: ", true);
1720
1721 run_test(r"program test.aleo; function dummy: ", r"program test.aleo; function bummy: ", false);
1723
1724 run_test(
1726 r"program test.aleo; function dummy: ",
1727 r"program test.aleo; constructor: assert.eq true true; function dummy: ",
1728 false,
1729 );
1730
1731 run_test(
1733 r"program test.aleo; struct foo: data as u8; function dummy:",
1734 r"program test.aleo; function dummy: struct foo: data as u8;",
1735 false,
1736 );
1737 }
1738
1739 #[test]
1740 fn test_contains_v14_syntax() -> Result<()> {
1741 let no_v14 = Program::<CurrentNetwork>::from_str(
1743 r"program test.aleo;
1744function foo:
1745 input r0 as u64.public;
1746 output r0 as u64.public;",
1747 )?;
1748 assert!(!no_v14.contains_v14_syntax()?);
1749
1750 let dynamic_record_input = Program::<CurrentNetwork>::from_str(
1752 r"program test.aleo;
1753function foo:
1754 input r0 as dynamic.record;",
1755 )?;
1756 assert!(dynamic_record_input.contains_v14_syntax()?);
1757
1758 let dynamic_future_output = Program::<CurrentNetwork>::from_str(
1760 r"program test.aleo;
1761function foo:
1762 output r0 as dynamic.future;",
1763 )?;
1764 assert!(dynamic_future_output.contains_v14_syntax()?);
1765
1766 let call_dynamic = Program::<CurrentNetwork>::from_str(
1768 r"program test.aleo;
1769function foo:
1770 input r0 as field.public;
1771 input r1 as field.public;
1772 input r2 as field.public;
1773 call.dynamic r0 r1 r2 into r3 (as u64.public);
1774 output r3 as u64.public;",
1775 )?;
1776 assert!(call_dynamic.contains_v14_syntax()?);
1777
1778 let get_record_dynamic = Program::<CurrentNetwork>::from_str(
1780 r"program test.aleo;
1781function foo:
1782 input r0 as dynamic.record;
1783 get.record.dynamic r0.amount into r1 as field;
1784 output r1 as field.public;",
1785 )?;
1786 assert!(get_record_dynamic.contains_v14_syntax()?);
1787
1788 let cast_dynamic_record = Program::<CurrentNetwork>::from_str(
1790 r"program test.aleo;
1791record token:
1792 owner as address.private;
1793 amount as u64.private;
1794function foo:
1795 input r0 as token.record;
1796 cast r0 into r1 as dynamic.record;
1797 output r0.owner as address.private;",
1798 )?;
1799 assert!(cast_dynamic_record.contains_v14_syntax()?);
1800
1801 let contains_dynamic = Program::<CurrentNetwork>::from_str(
1803 r"program test.aleo;
1804function bar:
1805 input r0 as field.public;
1806 input r1 as field.public;
1807 input r2 as field.public;
1808 input r3 as field.public;
1809 async bar r0 r1 r2 r3 into r4;
1810 output r4 as test.aleo/bar.future;
1811finalize bar:
1812 input r0 as field.public;
1813 input r1 as field.public;
1814 input r2 as field.public;
1815 input r3 as field.public;
1816 contains.dynamic r0 r1 r2[r3] into r4;",
1817 )?;
1818 assert!(contains_dynamic.contains_v14_syntax()?);
1819
1820 let get_dynamic = Program::<CurrentNetwork>::from_str(
1822 r"program test.aleo;
1823function bar:
1824 input r0 as field.public;
1825 input r1 as field.public;
1826 input r2 as field.public;
1827 input r3 as field.public;
1828 async bar r0 r1 r2 r3 into r4;
1829 output r4 as test.aleo/bar.future;
1830finalize bar:
1831 input r0 as field.public;
1832 input r1 as field.public;
1833 input r2 as field.public;
1834 input r3 as field.public;
1835 get.dynamic r0 r1 r2[r3] into r4 as field;",
1836 )?;
1837 assert!(get_dynamic.contains_v14_syntax()?);
1838
1839 let dynamic_future_finalize_input = Program::<CurrentNetwork>::from_str(
1841 r"program test.aleo;
1842function foo:
1843 input r0 as field.public;
1844 async foo r0 into r1;
1845 output r1 as test.aleo/foo.future;
1846finalize foo:
1847 input r0 as dynamic.future;
1848 await r0;",
1849 )?;
1850 assert!(dynamic_future_finalize_input.contains_v14_syntax()?);
1851
1852 let closure_dynamic_input = Program::<CurrentNetwork>::from_str(
1854 r"program test.aleo;
1855closure bar:
1856 input r0 as dynamic.record;
1857 input r1 as field;
1858 add r1 r1 into r2;",
1859 )?;
1860 assert!(closure_dynamic_input.contains_v14_syntax()?);
1861
1862 let closure_dynamic_output = Program::<CurrentNetwork>::from_str(
1864 r"program test.aleo;
1865closure bar:
1866 input r0 as field;
1867 add r0 r0 into r1;
1868 output r1 as dynamic.record;",
1869 )?;
1870 assert!(closure_dynamic_output.contains_v14_syntax()?);
1871
1872 let get_or_use_dynamic = Program::<CurrentNetwork>::from_str(
1874 r"program test.aleo;
1875function bar:
1876 input r0 as field.public;
1877 input r1 as field.public;
1878 input r2 as field.public;
1879 input r3 as field.public;
1880 input r4 as u64.public;
1881 async bar r0 r1 r2 r3 r4 into r5;
1882 output r5 as test.aleo/bar.future;
1883finalize bar:
1884 input r0 as field.public;
1885 input r1 as field.public;
1886 input r2 as field.public;
1887 input r3 as field.public;
1888 input r4 as u64.public;
1889 get.or_use.dynamic r0 r1 r2[r3] r4 into r5 as u64;",
1890 )?;
1891 assert!(get_or_use_dynamic.contains_v14_syntax()?);
1892
1893 let snark_verify = Program::<CurrentNetwork>::from_str(
1895 r"program test.aleo;
1896function foo:
1897 input r0 as [u8; 8u32].public;
1898 input r1 as [field; 1u32].public;
1899 input r2 as [u8; 8u32].public;
1900 async foo r0 r1 r2 into r3;
1901 output r3 as test.aleo/foo.future;
1902finalize foo:
1903 input r0 as [u8; 8u32].public;
1904 input r1 as [field; 1u32].public;
1905 input r2 as [u8; 8u32].public;
1906 snark.verify r0 1u8 r1 r2 into r3;",
1907 )?;
1908 assert!(snark_verify.contains_v14_syntax()?);
1909
1910 let snark_verify_batch = Program::<CurrentNetwork>::from_str(
1912 r"program test.aleo;
1913function foo:
1914 input r0 as [[u8; 8u32]; 1u32].public;
1915 input r1 as [[[field; 1u32]; 1u32]; 1u32].public;
1916 input r2 as [u8; 8u32].public;
1917 async foo r0 r1 r2 into r3;
1918 output r3 as test.aleo/foo.future;
1919finalize foo:
1920 input r0 as [[u8; 8u32]; 1u32].public;
1921 input r1 as [[[field; 1u32]; 1u32]; 1u32].public;
1922 input r2 as [u8; 8u32].public;
1923 snark.verify.batch r0 1u8 r1 r2 into r3;",
1924 )?;
1925 assert!(snark_verify_batch.contains_v14_syntax()?);
1926
1927 let aleo_generator = Program::<CurrentNetwork>::from_str(
1929 r"program test.aleo;
1930function foo:
1931 input r0 as scalar.public;
1932 mul aleo::GENERATOR r0 into r1;
1933 output r1 as group.public;",
1934 )?;
1935 assert!(aleo_generator.contains_v14_syntax()?);
1936
1937 let aleo_generator_powers = Program::<CurrentNetwork>::from_str(
1939 r"program test.aleo;
1940function foo:
1941 input r0 as scalar.public;
1942 mul aleo::GENERATOR_POWERS[0u32] r0 into r1;
1943 output r1 as group.public;",
1944 )?;
1945 assert!(aleo_generator_powers.contains_v14_syntax()?);
1946
1947 let closure_dynamic_future_output = Program::<CurrentNetwork>::from_str(
1949 r"program test.aleo;
1950closure bar:
1951 input r0 as field;
1952 add r0 r0 into r1;
1953 output r1 as dynamic.future;",
1954 )?;
1955 assert!(closure_dynamic_future_output.contains_v14_syntax()?);
1956
1957 let constructor_v14 = Program::<CurrentNetwork>::from_str(
1959 r"program test.aleo;
1960function dummy:
1961 input r0 as field.public;
1962 output r0 as field.public;
1963constructor:
1964 assert.eq aleo::GENERATOR aleo::GENERATOR;",
1965 )?;
1966 assert!(constructor_v14.contains_v14_syntax()?);
1967
1968 Ok(())
1969 }
1970}