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_string_type(&self) -> bool {
1445 self.mappings.values().any(|mapping| mapping.contains_string_type())
1446 || self.structs.values().any(|struct_type| struct_type.contains_string_type())
1447 || self.records.values().any(|record_type| record_type.contains_string_type())
1448 || self.closures.values().any(|closure| closure.contains_string_type())
1449 || self.functions.values().any(|function| function.contains_string_type())
1450 || self.constructor.iter().any(|constructor| constructor.contains_string_type())
1451 || self.views.values().any(|view| view.contains_string_type())
1452 }
1453}
1454
1455impl<N: Network> TypeName for ProgramCore<N> {
1456 #[inline]
1458 fn type_name() -> &'static str {
1459 "program"
1460 }
1461}
1462
1463#[cfg(test)]
1464mod tests {
1465 use super::*;
1466 use console::{
1467 network::MainnetV0,
1468 program::{Locator, ValueType},
1469 };
1470
1471 type CurrentNetwork = MainnetV0;
1472
1473 #[test]
1474 fn test_program_mapping() -> Result<()> {
1475 let mapping = Mapping::<CurrentNetwork>::from_str(
1477 r"
1478mapping message:
1479 key as field.public;
1480 value as field.public;",
1481 )?;
1482
1483 let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {mapping}"))?;
1485 assert!(program.contains_mapping(&Identifier::from_str("message")?));
1487 assert_eq!(mapping.to_string(), program.get_mapping(&Identifier::from_str("message")?)?.to_string());
1489
1490 Ok(())
1491 }
1492
1493 #[test]
1494 fn test_program_struct() -> Result<()> {
1495 let struct_ = StructType::<CurrentNetwork>::from_str(
1497 r"
1498struct message:
1499 first as field;
1500 second as field;",
1501 )?;
1502
1503 let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {struct_}"))?;
1505 assert!(program.contains_struct(&Identifier::from_str("message")?));
1507 assert_eq!(&struct_, program.get_struct(&Identifier::from_str("message")?)?);
1509
1510 Ok(())
1511 }
1512
1513 #[test]
1514 fn test_program_record() -> Result<()> {
1515 let record = RecordType::<CurrentNetwork>::from_str(
1517 r"
1518record foo:
1519 owner as address.private;
1520 first as field.private;
1521 second as field.public;",
1522 )?;
1523
1524 let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {record}"))?;
1526 assert!(program.contains_record(&Identifier::from_str("foo")?));
1528 assert_eq!(&record, program.get_record(&Identifier::from_str("foo")?)?);
1530
1531 Ok(())
1532 }
1533
1534 #[test]
1535 fn test_program_function() -> Result<()> {
1536 let function = Function::<CurrentNetwork>::from_str(
1538 r"
1539function compute:
1540 input r0 as field.public;
1541 input r1 as field.private;
1542 add r0 r1 into r2;
1543 output r2 as field.private;",
1544 )?;
1545
1546 let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {function}"))?;
1548 assert!(program.contains_function(&Identifier::from_str("compute")?));
1550 assert_eq!(function, program.get_function(&Identifier::from_str("compute")?)?);
1552
1553 Ok(())
1554 }
1555
1556 #[test]
1557 fn test_program_import() -> Result<()> {
1558 let program = Program::<CurrentNetwork>::from_str(
1560 r"
1561import eth.aleo;
1562import usdc.aleo;
1563
1564program swap.aleo;
1565
1566// The `swap` function transfers ownership of the record
1567// for token A to the record owner of token B, and vice-versa.
1568function swap:
1569 // Input the record for token A.
1570 input r0 as eth.aleo/eth.record;
1571 // Input the record for token B.
1572 input r1 as usdc.aleo/usdc.record;
1573
1574 // Send the record for token A to the owner of token B.
1575 call eth.aleo/transfer r0 r1.owner r0.amount into r2 r3;
1576
1577 // Send the record for token B to the owner of token A.
1578 call usdc.aleo/transfer r1 r0.owner r1.amount into r4 r5;
1579
1580 // Output the new record for token A.
1581 output r2 as eth.aleo/eth.record;
1582 // Output the new record for token B.
1583 output r4 as usdc.aleo/usdc.record;
1584 ",
1585 )
1586 .unwrap();
1587
1588 assert!(program.contains_import(&ProgramID::from_str("eth.aleo")?));
1590 assert!(program.contains_import(&ProgramID::from_str("usdc.aleo")?));
1591
1592 let function = program.get_function(&Identifier::from_str("swap")?)?;
1594
1595 assert_eq!(function.inputs().len(), 2);
1597 assert_eq!(function.input_types().len(), 2);
1598
1599 let expected_input_type_1 = ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?);
1601 let expected_input_type_2 = ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?);
1602
1603 assert_eq!(function.input_types()[0], expected_input_type_1);
1605 assert_eq!(function.input_types()[1], expected_input_type_2);
1606
1607 assert_eq!(function.input_types()[0].variant(), expected_input_type_1.variant());
1609 assert_eq!(function.input_types()[1].variant(), expected_input_type_2.variant());
1610
1611 assert_eq!(function.instructions().len(), 2);
1613
1614 assert_eq!(function.instructions()[0].opcode(), Opcode::Call("call"));
1616 assert_eq!(function.instructions()[1].opcode(), Opcode::Call("call"));
1617
1618 assert_eq!(function.outputs().len(), 2);
1620 assert_eq!(function.output_types().len(), 2);
1621
1622 let expected_output_type_1 = ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?);
1624 let expected_output_type_2 = ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?);
1625
1626 assert_eq!(function.output_types()[0], expected_output_type_1);
1628 assert_eq!(function.output_types()[1], expected_output_type_2);
1629
1630 assert_eq!(function.output_types()[0].variant(), expected_output_type_1.variant());
1632 assert_eq!(function.output_types()[1].variant(), expected_output_type_2.variant());
1633
1634 Ok(())
1635 }
1636
1637 #[test]
1638 fn test_program_with_constructor() {
1639 let program_string = r"import credits.aleo;
1641
1642program good_constructor.aleo;
1643
1644constructor:
1645 assert.eq edition 0u16;
1646 assert.eq credits.aleo/edition 0u16;
1647 assert.neq checksum 0field;
1648 assert.eq credits.aleo/checksum 6192738754253668739186185034243585975029374333074931926190215457304721124008field;
1649 set 1u8 into data[0u8];
1650
1651mapping data:
1652 key as u8.public;
1653 value as u8.public;
1654
1655function dummy:
1656
1657function check:
1658 async check into r0;
1659 output r0 as good_constructor.aleo/check.future;
1660
1661finalize check:
1662 get data[0u8] into r0;
1663 assert.eq r0 1u8;
1664";
1665 let program = Program::<CurrentNetwork>::from_str(program_string).unwrap();
1666
1667 let serialized = program.to_string();
1669 let deserialized = Program::<CurrentNetwork>::from_str(&serialized).unwrap();
1670 assert_eq!(program, deserialized);
1671
1672 let serialized = program.to_bytes_le().unwrap();
1673 let deserialized = Program::<CurrentNetwork>::from_bytes_le(&serialized).unwrap();
1674 assert_eq!(program, deserialized);
1675
1676 let display = format!("{program}");
1678 assert_eq!(display, program_string);
1679
1680 assert!(program.contains_constructor());
1682 assert_eq!(program.constructor().unwrap().commands().len(), 5);
1683 }
1684
1685 #[test]
1686 fn test_program_equality_and_checksum() {
1687 fn run_test(program1: &str, program2: &str, expected_equal: bool) {
1688 println!("Comparing programs:\n{program1}\n{program2}");
1689 let program1 = Program::<CurrentNetwork>::from_str(program1).unwrap();
1690 let program2 = Program::<CurrentNetwork>::from_str(program2).unwrap();
1691 assert_eq!(program1 == program2, expected_equal);
1692 assert_eq!(program1.to_checksum() == program2.to_checksum(), expected_equal);
1693 }
1694
1695 run_test(r"program test.aleo; function dummy: ", r"program test.aleo; function dummy: ", true);
1697
1698 run_test(r"program test.aleo; function dummy: ", r"program test.aleo; function bummy: ", false);
1700
1701 run_test(
1703 r"program test.aleo; function dummy: ",
1704 r"program test.aleo; constructor: assert.eq true true; function dummy: ",
1705 false,
1706 );
1707
1708 run_test(
1710 r"program test.aleo; struct foo: data as u8; function dummy:",
1711 r"program test.aleo; function dummy: struct foo: data as u8;",
1712 false,
1713 );
1714 }
1715
1716 #[test]
1717 fn test_contains_v14_syntax() -> Result<()> {
1718 let no_v14 = Program::<CurrentNetwork>::from_str(
1720 r"program test.aleo;
1721function foo:
1722 input r0 as u64.public;
1723 output r0 as u64.public;",
1724 )?;
1725 assert!(!no_v14.contains_v14_syntax()?);
1726
1727 let dynamic_record_input = Program::<CurrentNetwork>::from_str(
1729 r"program test.aleo;
1730function foo:
1731 input r0 as dynamic.record;",
1732 )?;
1733 assert!(dynamic_record_input.contains_v14_syntax()?);
1734
1735 let dynamic_future_output = Program::<CurrentNetwork>::from_str(
1737 r"program test.aleo;
1738function foo:
1739 output r0 as dynamic.future;",
1740 )?;
1741 assert!(dynamic_future_output.contains_v14_syntax()?);
1742
1743 let call_dynamic = Program::<CurrentNetwork>::from_str(
1745 r"program test.aleo;
1746function foo:
1747 input r0 as field.public;
1748 input r1 as field.public;
1749 input r2 as field.public;
1750 call.dynamic r0 r1 r2 into r3 (as u64.public);
1751 output r3 as u64.public;",
1752 )?;
1753 assert!(call_dynamic.contains_v14_syntax()?);
1754
1755 let get_record_dynamic = Program::<CurrentNetwork>::from_str(
1757 r"program test.aleo;
1758function foo:
1759 input r0 as dynamic.record;
1760 get.record.dynamic r0.amount into r1 as field;
1761 output r1 as field.public;",
1762 )?;
1763 assert!(get_record_dynamic.contains_v14_syntax()?);
1764
1765 let cast_dynamic_record = Program::<CurrentNetwork>::from_str(
1767 r"program test.aleo;
1768record token:
1769 owner as address.private;
1770 amount as u64.private;
1771function foo:
1772 input r0 as token.record;
1773 cast r0 into r1 as dynamic.record;
1774 output r0.owner as address.private;",
1775 )?;
1776 assert!(cast_dynamic_record.contains_v14_syntax()?);
1777
1778 let contains_dynamic = Program::<CurrentNetwork>::from_str(
1780 r"program test.aleo;
1781function bar:
1782 input r0 as field.public;
1783 input r1 as field.public;
1784 input r2 as field.public;
1785 input r3 as field.public;
1786 async bar r0 r1 r2 r3 into r4;
1787 output r4 as test.aleo/bar.future;
1788finalize bar:
1789 input r0 as field.public;
1790 input r1 as field.public;
1791 input r2 as field.public;
1792 input r3 as field.public;
1793 contains.dynamic r0 r1 r2[r3] into r4;",
1794 )?;
1795 assert!(contains_dynamic.contains_v14_syntax()?);
1796
1797 let get_dynamic = Program::<CurrentNetwork>::from_str(
1799 r"program test.aleo;
1800function bar:
1801 input r0 as field.public;
1802 input r1 as field.public;
1803 input r2 as field.public;
1804 input r3 as field.public;
1805 async bar r0 r1 r2 r3 into r4;
1806 output r4 as test.aleo/bar.future;
1807finalize bar:
1808 input r0 as field.public;
1809 input r1 as field.public;
1810 input r2 as field.public;
1811 input r3 as field.public;
1812 get.dynamic r0 r1 r2[r3] into r4 as field;",
1813 )?;
1814 assert!(get_dynamic.contains_v14_syntax()?);
1815
1816 let dynamic_future_finalize_input = Program::<CurrentNetwork>::from_str(
1818 r"program test.aleo;
1819function foo:
1820 input r0 as field.public;
1821 async foo r0 into r1;
1822 output r1 as test.aleo/foo.future;
1823finalize foo:
1824 input r0 as dynamic.future;
1825 await r0;",
1826 )?;
1827 assert!(dynamic_future_finalize_input.contains_v14_syntax()?);
1828
1829 let closure_dynamic_input = Program::<CurrentNetwork>::from_str(
1831 r"program test.aleo;
1832closure bar:
1833 input r0 as dynamic.record;
1834 input r1 as field;
1835 add r1 r1 into r2;",
1836 )?;
1837 assert!(closure_dynamic_input.contains_v14_syntax()?);
1838
1839 let closure_dynamic_output = Program::<CurrentNetwork>::from_str(
1841 r"program test.aleo;
1842closure bar:
1843 input r0 as field;
1844 add r0 r0 into r1;
1845 output r1 as dynamic.record;",
1846 )?;
1847 assert!(closure_dynamic_output.contains_v14_syntax()?);
1848
1849 let get_or_use_dynamic = Program::<CurrentNetwork>::from_str(
1851 r"program test.aleo;
1852function bar:
1853 input r0 as field.public;
1854 input r1 as field.public;
1855 input r2 as field.public;
1856 input r3 as field.public;
1857 input r4 as u64.public;
1858 async bar r0 r1 r2 r3 r4 into r5;
1859 output r5 as test.aleo/bar.future;
1860finalize bar:
1861 input r0 as field.public;
1862 input r1 as field.public;
1863 input r2 as field.public;
1864 input r3 as field.public;
1865 input r4 as u64.public;
1866 get.or_use.dynamic r0 r1 r2[r3] r4 into r5 as u64;",
1867 )?;
1868 assert!(get_or_use_dynamic.contains_v14_syntax()?);
1869
1870 let snark_verify = Program::<CurrentNetwork>::from_str(
1872 r"program test.aleo;
1873function foo:
1874 input r0 as [u8; 8u32].public;
1875 input r1 as [field; 1u32].public;
1876 input r2 as [u8; 8u32].public;
1877 async foo r0 r1 r2 into r3;
1878 output r3 as test.aleo/foo.future;
1879finalize foo:
1880 input r0 as [u8; 8u32].public;
1881 input r1 as [field; 1u32].public;
1882 input r2 as [u8; 8u32].public;
1883 snark.verify r0 1u8 r1 r2 into r3;",
1884 )?;
1885 assert!(snark_verify.contains_v14_syntax()?);
1886
1887 let snark_verify_batch = Program::<CurrentNetwork>::from_str(
1889 r"program test.aleo;
1890function foo:
1891 input r0 as [[u8; 8u32]; 1u32].public;
1892 input r1 as [[[field; 1u32]; 1u32]; 1u32].public;
1893 input r2 as [u8; 8u32].public;
1894 async foo r0 r1 r2 into r3;
1895 output r3 as test.aleo/foo.future;
1896finalize foo:
1897 input r0 as [[u8; 8u32]; 1u32].public;
1898 input r1 as [[[field; 1u32]; 1u32]; 1u32].public;
1899 input r2 as [u8; 8u32].public;
1900 snark.verify.batch r0 1u8 r1 r2 into r3;",
1901 )?;
1902 assert!(snark_verify_batch.contains_v14_syntax()?);
1903
1904 let aleo_generator = Program::<CurrentNetwork>::from_str(
1906 r"program test.aleo;
1907function foo:
1908 input r0 as scalar.public;
1909 mul aleo::GENERATOR r0 into r1;
1910 output r1 as group.public;",
1911 )?;
1912 assert!(aleo_generator.contains_v14_syntax()?);
1913
1914 let aleo_generator_powers = Program::<CurrentNetwork>::from_str(
1916 r"program test.aleo;
1917function foo:
1918 input r0 as scalar.public;
1919 mul aleo::GENERATOR_POWERS[0u32] r0 into r1;
1920 output r1 as group.public;",
1921 )?;
1922 assert!(aleo_generator_powers.contains_v14_syntax()?);
1923
1924 let closure_dynamic_future_output = Program::<CurrentNetwork>::from_str(
1926 r"program test.aleo;
1927closure bar:
1928 input r0 as field;
1929 add r0 r0 into r1;
1930 output r1 as dynamic.future;",
1931 )?;
1932 assert!(closure_dynamic_future_output.contains_v14_syntax()?);
1933
1934 let constructor_v14 = Program::<CurrentNetwork>::from_str(
1936 r"program test.aleo;
1937function dummy:
1938 input r0 as field.public;
1939 output r0 as field.public;
1940constructor:
1941 assert.eq aleo::GENERATOR aleo::GENERATOR;",
1942 )?;
1943 assert!(constructor_v14.contains_v14_syntax()?);
1944
1945 Ok(())
1946 }
1947}