1#![forbid(unsafe_code)]
17#![allow(clippy::too_many_arguments)]
18#![warn(clippy::cast_possible_truncation)]
19
20pub type Program<N> = crate::ProgramCore<N, Instruction<N>, Command<N>>;
21pub type Function<N> = crate::FunctionCore<N, Instruction<N>, Command<N>>;
22pub type Finalize<N> = crate::FinalizeCore<N, Command<N>>;
23pub type Closure<N> = crate::ClosureCore<N, Instruction<N>>;
24
25mod closure;
26pub use closure::*;
27
28pub mod finalize;
29pub use finalize::*;
30
31mod function;
32pub use function::*;
33
34mod import;
35pub use import::*;
36
37pub mod logic;
38pub use logic::*;
39
40mod mapping;
41pub use mapping::*;
42
43pub mod traits;
44pub use traits::*;
45
46mod bytes;
47mod parse;
48mod serialize;
49
50use console::{
51 network::prelude::{
52 Debug,
53 Deserialize,
54 Deserializer,
55 Display,
56 Err,
57 Error,
58 ErrorKind,
59 Formatter,
60 FromBytes,
61 FromBytesDeserializer,
62 FromStr,
63 IoResult,
64 Network,
65 Parser,
66 ParserResult,
67 Read,
68 Result,
69 Sanitizer,
70 Serialize,
71 Serializer,
72 ToBytes,
73 ToBytesSerializer,
74 TypeName,
75 Write,
76 anyhow,
77 bail,
78 de,
79 ensure,
80 error,
81 fmt,
82 make_error,
83 many0,
84 many1,
85 map,
86 map_res,
87 tag,
88 take,
89 },
90 program::{Identifier, PlaintextType, ProgramID, RecordType, StructType},
91};
92
93use indexmap::IndexMap;
94
95#[derive(Copy, Clone, PartialEq, Eq, Hash)]
96enum ProgramDefinition {
97 Mapping,
99 Struct,
101 Record,
103 Closure,
105 Function,
107}
108
109#[derive(Clone, PartialEq, Eq)]
110pub struct ProgramCore<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> {
111 id: ProgramID<N>,
113 imports: IndexMap<ProgramID<N>, Import<N>>,
115 identifiers: IndexMap<Identifier<N>, ProgramDefinition>,
117 mappings: IndexMap<Identifier<N>, Mapping<N>>,
119 structs: IndexMap<Identifier<N>, StructType<N>>,
121 records: IndexMap<Identifier<N>, RecordType<N>>,
123 closures: IndexMap<Identifier<N>, ClosureCore<N, Instruction>>,
125 functions: IndexMap<Identifier<N>, FunctionCore<N, Instruction, Command>>,
127}
128
129impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> ProgramCore<N, Instruction, Command> {
130 #[inline]
132 pub fn new(id: ProgramID<N>) -> Result<Self> {
133 ensure!(!Self::is_reserved_keyword(id.name()), "Program name is invalid: {}", id.name());
135
136 Ok(Self {
137 id,
138 imports: IndexMap::new(),
139 identifiers: IndexMap::new(),
140 mappings: IndexMap::new(),
141 structs: IndexMap::new(),
142 records: IndexMap::new(),
143 closures: IndexMap::new(),
144 functions: IndexMap::new(),
145 })
146 }
147
148 #[inline]
150 pub fn credits() -> Result<Self> {
151 Self::from_str(include_str!("./resources/credits.aleo"))
152 }
153
154 pub const fn id(&self) -> &ProgramID<N> {
156 &self.id
157 }
158
159 pub const fn imports(&self) -> &IndexMap<ProgramID<N>, Import<N>> {
161 &self.imports
162 }
163
164 pub const fn mappings(&self) -> &IndexMap<Identifier<N>, Mapping<N>> {
166 &self.mappings
167 }
168
169 pub const fn structs(&self) -> &IndexMap<Identifier<N>, StructType<N>> {
171 &self.structs
172 }
173
174 pub const fn records(&self) -> &IndexMap<Identifier<N>, RecordType<N>> {
176 &self.records
177 }
178
179 pub const fn closures(&self) -> &IndexMap<Identifier<N>, ClosureCore<N, Instruction>> {
181 &self.closures
182 }
183
184 pub const fn functions(&self) -> &IndexMap<Identifier<N>, FunctionCore<N, Instruction, Command>> {
186 &self.functions
187 }
188
189 pub fn contains_import(&self, id: &ProgramID<N>) -> bool {
191 self.imports.contains_key(id)
192 }
193
194 pub fn contains_mapping(&self, name: &Identifier<N>) -> bool {
196 self.mappings.contains_key(name)
197 }
198
199 pub fn contains_struct(&self, name: &Identifier<N>) -> bool {
201 self.structs.contains_key(name)
202 }
203
204 pub fn contains_record(&self, name: &Identifier<N>) -> bool {
206 self.records.contains_key(name)
207 }
208
209 pub fn contains_closure(&self, name: &Identifier<N>) -> bool {
211 self.closures.contains_key(name)
212 }
213
214 pub fn contains_function(&self, name: &Identifier<N>) -> bool {
216 self.functions.contains_key(name)
217 }
218
219 pub fn get_mapping(&self, name: &Identifier<N>) -> Result<Mapping<N>> {
221 let mapping = self.mappings.get(name).cloned().ok_or_else(|| anyhow!("Mapping '{name}' is not defined."))?;
223 ensure!(mapping.name() == name, "Expected mapping '{name}', but found mapping '{}'", mapping.name());
225 Ok(mapping)
227 }
228
229 pub fn get_struct(&self, name: &Identifier<N>) -> Result<&StructType<N>> {
231 let struct_ = self.structs.get(name).ok_or_else(|| anyhow!("Struct '{name}' is not defined."))?;
233 ensure!(struct_.name() == name, "Expected struct '{name}', but found struct '{}'", struct_.name());
235 ensure!(!struct_.members().is_empty(), "Struct '{name}' is missing members.");
237 Ok(struct_)
239 }
240
241 pub fn get_record(&self, name: &Identifier<N>) -> Result<&RecordType<N>> {
243 let record = self.records.get(name).ok_or_else(|| anyhow!("Record '{name}' is not defined."))?;
245 ensure!(record.name() == name, "Expected record '{name}', but found record '{}'", record.name());
247 Ok(record)
249 }
250
251 pub fn get_closure(&self, name: &Identifier<N>) -> Result<ClosureCore<N, Instruction>> {
253 let closure = self.closures.get(name).cloned().ok_or_else(|| anyhow!("Closure '{name}' is not defined."))?;
255 ensure!(closure.name() == name, "Expected closure '{name}', but found closure '{}'", closure.name());
257 ensure!(!closure.inputs().is_empty(), "Cannot evaluate a closure without input statements");
259 ensure!(closure.inputs().len() <= N::MAX_INPUTS, "Closure exceeds maximum number of inputs");
261 ensure!(!closure.instructions().is_empty(), "Cannot evaluate a closure without instructions");
263 ensure!(closure.outputs().len() <= N::MAX_OUTPUTS, "Closure exceeds maximum number of outputs");
265 Ok(closure)
267 }
268
269 pub fn get_function(&self, name: &Identifier<N>) -> Result<FunctionCore<N, Instruction, Command>> {
271 self.get_function_ref(name).cloned()
272 }
273
274 pub fn get_function_ref(&self, name: &Identifier<N>) -> Result<&FunctionCore<N, Instruction, Command>> {
276 let function = self.functions.get(name).ok_or(anyhow!("Function '{}/{name}' is not defined.", self.id))?;
278 ensure!(function.name() == name, "Expected function '{name}', but found function '{}'", function.name());
280 ensure!(function.inputs().len() <= N::MAX_INPUTS, "Function exceeds maximum number of inputs");
282 ensure!(function.instructions().len() <= N::MAX_INSTRUCTIONS, "Function exceeds maximum instructions");
284 ensure!(function.outputs().len() <= N::MAX_OUTPUTS, "Function exceeds maximum number of outputs");
286 Ok(function)
288 }
289}
290
291impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> ProgramCore<N, Instruction, Command> {
292 #[inline]
297 fn add_import(&mut self, import: Import<N>) -> Result<()> {
298 let import_name = *import.name();
300
301 ensure!(self.imports.len() < N::MAX_IMPORTS, "Program exceeds the maximum number of imports");
303
304 ensure!(self.is_unique_name(&import_name), "'{import_name}' is already in use.");
306 ensure!(!Self::is_reserved_opcode(&import_name.to_string()), "'{import_name}' is a reserved opcode.");
308 ensure!(!Self::is_reserved_keyword(&import_name), "'{import_name}' is a reserved keyword.");
310
311 ensure!(
313 !self.imports.contains_key(import.program_id()),
314 "Import '{}' is already defined.",
315 import.program_id()
316 );
317
318 if self.imports.insert(*import.program_id(), import.clone()).is_some() {
320 bail!("'{}' already exists in the program.", import.program_id())
321 }
322 Ok(())
323 }
324
325 #[inline]
331 fn add_mapping(&mut self, mapping: Mapping<N>) -> Result<()> {
332 let mapping_name = *mapping.name();
334
335 ensure!(self.mappings.len() < N::MAX_MAPPINGS, "Program exceeds the maximum number of mappings");
337
338 ensure!(self.is_unique_name(&mapping_name), "'{mapping_name}' is already in use.");
340 ensure!(!Self::is_reserved_keyword(&mapping_name), "'{mapping_name}' is a reserved keyword.");
342 ensure!(!Self::is_reserved_opcode(&mapping_name.to_string()), "'{mapping_name}' is a reserved opcode.");
344
345 if self.identifiers.insert(mapping_name, ProgramDefinition::Mapping).is_some() {
347 bail!("'{mapping_name}' already exists in the program.")
348 }
349 if self.mappings.insert(mapping_name, mapping).is_some() {
351 bail!("'{mapping_name}' already exists in the program.")
352 }
353 Ok(())
354 }
355
356 #[inline]
364 fn add_struct(&mut self, struct_: StructType<N>) -> Result<()> {
365 let struct_name = *struct_.name();
367
368 ensure!(self.structs.len() < N::MAX_STRUCTS, "Program exceeds the maximum number of structs.");
370
371 ensure!(self.is_unique_name(&struct_name), "'{struct_name}' is already in use.");
373 ensure!(!Self::is_reserved_opcode(&struct_name.to_string()), "'{struct_name}' is a reserved opcode.");
375 ensure!(!Self::is_reserved_keyword(&struct_name), "'{struct_name}' is a reserved keyword.");
377
378 ensure!(!struct_.members().is_empty(), "Struct '{struct_name}' is missing members.");
380
381 for (identifier, plaintext_type) in struct_.members() {
384 ensure!(!Self::is_reserved_keyword(identifier), "'{identifier}' is a reserved keyword.");
386 match plaintext_type {
388 PlaintextType::Literal(_) => continue,
389 PlaintextType::Struct(member_identifier) => {
390 if !self.structs.contains_key(member_identifier) {
392 bail!("'{member_identifier}' in struct '{}' is not defined.", struct_name)
393 }
394 }
395 PlaintextType::Array(array_type) => {
396 if let PlaintextType::Struct(struct_name) = array_type.base_element_type() {
397 if !self.structs.contains_key(struct_name) {
399 bail!("'{struct_name}' in array '{array_type}' is not defined.")
400 }
401 }
402 }
403 }
404 }
405
406 if self.identifiers.insert(struct_name, ProgramDefinition::Struct).is_some() {
408 bail!("'{}' already exists in the program.", struct_name)
409 }
410 if self.structs.insert(struct_name, struct_).is_some() {
412 bail!("'{}' already exists in the program.", struct_name)
413 }
414 Ok(())
415 }
416
417 #[inline]
425 fn add_record(&mut self, record: RecordType<N>) -> Result<()> {
426 let record_name = *record.name();
428
429 ensure!(self.records.len() < N::MAX_RECORDS, "Program exceeds the maximum number of records.");
431
432 ensure!(self.is_unique_name(&record_name), "'{record_name}' is already in use.");
434 ensure!(!Self::is_reserved_opcode(&record_name.to_string()), "'{record_name}' is a reserved opcode.");
436 ensure!(!Self::is_reserved_keyword(&record_name), "'{record_name}' is a reserved keyword.");
438
439 for (identifier, entry_type) in record.entries() {
442 ensure!(!Self::is_reserved_keyword(identifier), "'{identifier}' is a reserved keyword.");
444 match entry_type.plaintext_type() {
446 PlaintextType::Literal(_) => continue,
447 PlaintextType::Struct(identifier) => {
448 if !self.structs.contains_key(identifier) {
449 bail!("Struct '{identifier}' in record '{record_name}' is not defined.")
450 }
451 }
452 PlaintextType::Array(array_type) => {
453 if let PlaintextType::Struct(struct_name) = array_type.base_element_type() {
454 if !self.structs.contains_key(struct_name) {
456 bail!("'{struct_name}' in array '{array_type}' is not defined.")
457 }
458 }
459 }
460 }
461 }
462
463 if self.identifiers.insert(record_name, ProgramDefinition::Record).is_some() {
465 bail!("'{record_name}' already exists in the program.")
466 }
467 if self.records.insert(record_name, record).is_some() {
469 bail!("'{record_name}' already exists in the program.")
470 }
471 Ok(())
472 }
473
474 #[inline]
488 fn add_closure(&mut self, closure: ClosureCore<N, Instruction>) -> Result<()> {
489 let closure_name = *closure.name();
491
492 ensure!(self.closures.len() < N::MAX_CLOSURES, "Program exceeds the maximum number of closures.");
494
495 ensure!(self.is_unique_name(&closure_name), "'{closure_name}' is already in use.");
497 ensure!(!Self::is_reserved_opcode(&closure_name.to_string()), "'{closure_name}' is a reserved opcode.");
499 ensure!(!Self::is_reserved_keyword(&closure_name), "'{closure_name}' is a reserved keyword.");
501
502 ensure!(!closure.inputs().is_empty(), "Cannot evaluate a closure without input statements");
504 ensure!(closure.inputs().len() <= N::MAX_INPUTS, "Closure exceeds maximum number of inputs");
506 ensure!(!closure.instructions().is_empty(), "Cannot evaluate a closure without instructions");
508 ensure!(closure.outputs().len() <= N::MAX_OUTPUTS, "Closure exceeds maximum number of outputs");
510
511 if self.identifiers.insert(closure_name, ProgramDefinition::Closure).is_some() {
513 bail!("'{closure_name}' already exists in the program.")
514 }
515 if self.closures.insert(closure_name, closure).is_some() {
517 bail!("'{closure_name}' already exists in the program.")
518 }
519 Ok(())
520 }
521
522 #[inline]
536 fn add_function(&mut self, function: FunctionCore<N, Instruction, Command>) -> Result<()> {
537 let function_name = *function.name();
539
540 ensure!(self.functions.len() < N::MAX_FUNCTIONS, "Program exceeds the maximum number of functions");
542
543 ensure!(self.is_unique_name(&function_name), "'{function_name}' is already in use.");
545 ensure!(!Self::is_reserved_opcode(&function_name.to_string()), "'{function_name}' is a reserved opcode.");
547 ensure!(!Self::is_reserved_keyword(&function_name), "'{function_name}' is a reserved keyword.");
549
550 ensure!(function.inputs().len() <= N::MAX_INPUTS, "Function exceeds maximum number of inputs");
552 ensure!(function.instructions().len() <= N::MAX_INSTRUCTIONS, "Function exceeds maximum instructions");
554 ensure!(function.outputs().len() <= N::MAX_OUTPUTS, "Function exceeds maximum number of outputs");
556
557 if self.identifiers.insert(function_name, ProgramDefinition::Function).is_some() {
559 bail!("'{function_name}' already exists in the program.")
560 }
561 if self.functions.insert(function_name, function).is_some() {
563 bail!("'{function_name}' already exists in the program.")
564 }
565 Ok(())
566 }
567}
568
569impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> ProgramCore<N, Instruction, Command> {
570 #[rustfmt::skip]
571 const KEYWORDS: &'static [&'static str] = &[
572 "const",
574 "constant",
575 "public",
576 "private",
577 "address",
579 "boolean",
580 "field",
581 "group",
582 "i8",
583 "i16",
584 "i32",
585 "i64",
586 "i128",
587 "u8",
588 "u16",
589 "u32",
590 "u64",
591 "u128",
592 "scalar",
593 "signature",
594 "string",
595 "true",
597 "false",
598 "input",
600 "output",
601 "as",
602 "into",
603 "record",
605 "owner",
606 "transition",
608 "import",
609 "function",
610 "struct",
611 "closure",
612 "program",
613 "aleo",
614 "self",
615 "storage",
616 "mapping",
617 "key",
618 "value",
619 "async",
620 "finalize",
621 "global",
623 "block",
624 "return",
625 "break",
626 "assert",
627 "continue",
628 "let",
629 "if",
630 "else",
631 "while",
632 "for",
633 "switch",
634 "case",
635 "default",
636 "match",
637 "enum",
638 "struct",
639 "union",
640 "trait",
641 "impl",
642 "type",
643 "future",
644 ];
645
646 fn is_unique_name(&self, name: &Identifier<N>) -> bool {
648 !self.identifiers.contains_key(name)
649 }
650
651 pub fn is_reserved_opcode(name: &str) -> bool {
653 Instruction::is_reserved_opcode(name)
654 }
655
656 pub fn is_reserved_keyword(name: &Identifier<N>) -> bool {
658 let name = name.to_string();
660 Self::KEYWORDS.iter().any(|keyword| *keyword == name)
662 }
663}
664
665impl<N: Network, Instruction: InstructionTrait<N>, Command: CommandTrait<N>> TypeName
666 for ProgramCore<N, Instruction, Command>
667{
668 #[inline]
670 fn type_name() -> &'static str {
671 "program"
672 }
673}
674
675#[cfg(test)]
676mod tests {
677 use super::*;
678 use console::{
679 network::MainnetV0,
680 program::{Locator, ValueType},
681 };
682
683 type CurrentNetwork = MainnetV0;
684
685 #[test]
686 fn test_program_mapping() -> Result<()> {
687 let mapping = Mapping::<CurrentNetwork>::from_str(
689 r"
690mapping message:
691 key as field.public;
692 value as field.public;",
693 )?;
694
695 let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {mapping}"))?;
697 assert!(program.contains_mapping(&Identifier::from_str("message")?));
699 assert_eq!(mapping.to_string(), program.get_mapping(&Identifier::from_str("message")?)?.to_string());
701
702 Ok(())
703 }
704
705 #[test]
706 fn test_program_struct() -> Result<()> {
707 let struct_ = StructType::<CurrentNetwork>::from_str(
709 r"
710struct message:
711 first as field;
712 second as field;",
713 )?;
714
715 let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {struct_}"))?;
717 assert!(program.contains_struct(&Identifier::from_str("message")?));
719 assert_eq!(&struct_, program.get_struct(&Identifier::from_str("message")?)?);
721
722 Ok(())
723 }
724
725 #[test]
726 fn test_program_record() -> Result<()> {
727 let record = RecordType::<CurrentNetwork>::from_str(
729 r"
730record foo:
731 owner as address.private;
732 first as field.private;
733 second as field.public;",
734 )?;
735
736 let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {record}"))?;
738 assert!(program.contains_record(&Identifier::from_str("foo")?));
740 assert_eq!(&record, program.get_record(&Identifier::from_str("foo")?)?);
742
743 Ok(())
744 }
745
746 #[test]
747 fn test_program_function() -> Result<()> {
748 let function = Function::<CurrentNetwork>::from_str(
750 r"
751function compute:
752 input r0 as field.public;
753 input r1 as field.private;
754 add r0 r1 into r2;
755 output r2 as field.private;",
756 )?;
757
758 let program = Program::<CurrentNetwork>::from_str(&format!("program unknown.aleo; {function}"))?;
760 assert!(program.contains_function(&Identifier::from_str("compute")?));
762 assert_eq!(function, program.get_function(&Identifier::from_str("compute")?)?);
764
765 Ok(())
766 }
767
768 #[test]
769 fn test_program_import() -> Result<()> {
770 let program = Program::<CurrentNetwork>::from_str(
772 r"
773import eth.aleo;
774import usdc.aleo;
775
776program swap.aleo;
777
778// The `swap` function transfers ownership of the record
779// for token A to the record owner of token B, and vice-versa.
780function swap:
781 // Input the record for token A.
782 input r0 as eth.aleo/eth.record;
783 // Input the record for token B.
784 input r1 as usdc.aleo/usdc.record;
785
786 // Send the record for token A to the owner of token B.
787 call eth.aleo/transfer r0 r1.owner r0.amount into r2 r3;
788
789 // Send the record for token B to the owner of token A.
790 call usdc.aleo/transfer r1 r0.owner r1.amount into r4 r5;
791
792 // Output the new record for token A.
793 output r2 as eth.aleo/eth.record;
794 // Output the new record for token B.
795 output r4 as usdc.aleo/usdc.record;
796 ",
797 )
798 .unwrap();
799
800 assert!(program.contains_import(&ProgramID::from_str("eth.aleo")?));
802 assert!(program.contains_import(&ProgramID::from_str("usdc.aleo")?));
803
804 let function = program.get_function(&Identifier::from_str("swap")?)?;
806
807 assert_eq!(function.inputs().len(), 2);
809 assert_eq!(function.input_types().len(), 2);
810
811 let expected_input_type_1 = ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?);
813 let expected_input_type_2 = ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?);
814
815 assert_eq!(function.input_types()[0], expected_input_type_1);
817 assert_eq!(function.input_types()[1], expected_input_type_2);
818
819 assert_eq!(function.input_types()[0].variant(), expected_input_type_1.variant());
821 assert_eq!(function.input_types()[1].variant(), expected_input_type_2.variant());
822
823 assert_eq!(function.instructions().len(), 2);
825
826 assert_eq!(function.instructions()[0].opcode(), Opcode::Call);
828 assert_eq!(function.instructions()[1].opcode(), Opcode::Call);
829
830 assert_eq!(function.outputs().len(), 2);
832 assert_eq!(function.output_types().len(), 2);
833
834 let expected_output_type_1 = ValueType::ExternalRecord(Locator::from_str("eth.aleo/eth")?);
836 let expected_output_type_2 = ValueType::ExternalRecord(Locator::from_str("usdc.aleo/usdc")?);
837
838 assert_eq!(function.output_types()[0], expected_output_type_1);
840 assert_eq!(function.output_types()[1], expected_output_type_2);
841
842 assert_eq!(function.output_types()[0].variant(), expected_output_type_1.variant());
844 assert_eq!(function.output_types()[1].variant(), expected_output_type_2.variant());
845
846 Ok(())
847 }
848}