snarkvm_synthesizer_program/closure/
mod.rs1use crate::Instruction;
17
18mod input;
19use input::*;
20
21mod output;
22use output::*;
23
24mod bytes;
25mod parse;
26
27use console::{
28 network::{error, prelude::*},
29 program::{Identifier, Register, RegisterType},
30};
31
32use indexmap::IndexSet;
33
34#[derive(Clone, PartialEq, Eq)]
35pub struct ClosureCore<N: Network> {
36 name: Identifier<N>,
38 inputs: IndexSet<Input<N>>,
41 instructions: Vec<Instruction<N>>,
43 outputs: IndexSet<Output<N>>,
45}
46
47impl<N: Network> ClosureCore<N> {
48 pub fn new(name: Identifier<N>) -> Self {
50 Self { name, inputs: IndexSet::new(), instructions: Vec::new(), outputs: IndexSet::new() }
51 }
52
53 pub const fn name(&self) -> &Identifier<N> {
55 &self.name
56 }
57
58 pub fn to_checksum(&self) -> [console::types::U8<N>; 32] {
63 crate::to_checksum::source_code_checksum(&self.to_string())
64 }
65
66 pub const fn inputs(&self) -> &IndexSet<Input<N>> {
68 &self.inputs
69 }
70
71 pub fn instructions(&self) -> &[Instruction<N>] {
73 &self.instructions
74 }
75
76 pub const fn outputs(&self) -> &IndexSet<Output<N>> {
78 &self.outputs
79 }
80
81 pub fn output_types(&self) -> Vec<RegisterType<N>> {
83 self.outputs.iter().map(|output| output.register_type()).cloned().collect()
84 }
85
86 pub fn contains_external_struct(&self) -> bool {
88 self.inputs.iter().any(|input| input.register_type().contains_external_struct())
89 || self.outputs.iter().any(|output| output.register_type().contains_external_struct())
90 || self.instructions.iter().any(|instruction| instruction.contains_external_struct())
91 }
92
93 pub fn contains_string_type(&self) -> bool {
96 self.instructions.iter().any(|instruction| instruction.contains_string_type())
97 }
98
99 pub fn contains_identifier_type(&self) -> Result<bool> {
101 for input in &self.inputs {
102 if input.register_type().contains_identifier_type()? {
103 return Ok(true);
104 }
105 }
106 for output in &self.outputs {
107 if output.register_type().contains_identifier_type()? {
108 return Ok(true);
109 }
110 }
111 for instruction in &self.instructions {
113 if instruction.contains_identifier_type()? {
114 return Ok(true);
115 }
116 }
117 Ok(false)
118 }
119
120 pub fn exceeds_max_array_size(&self, max_array_size: u32) -> bool {
122 self.inputs.iter().any(|input| input.register_type().exceeds_max_array_size(max_array_size))
123 || self.outputs.iter().any(|output| output.register_type().exceeds_max_array_size(max_array_size))
124 || self.instructions.iter().any(|instruction| instruction.exceeds_max_array_size(max_array_size))
125 }
126}
127
128impl<N: Network> ClosureCore<N> {
129 #[inline]
136 fn add_input(&mut self, input: Input<N>) -> Result<()> {
137 ensure!(self.instructions.is_empty(), "Cannot add inputs after instructions have been added");
139 ensure!(self.outputs.is_empty(), "Cannot add inputs after outputs have been added");
140
141 ensure!(self.inputs.len() < N::MAX_INPUTS, "Cannot add more than {} inputs", N::MAX_INPUTS);
143 ensure!(!self.inputs.contains(&input), "Cannot add duplicate input statement");
145
146 ensure!(matches!(input.register(), Register::Locator(..)), "Input register must be a locator");
148
149 self.inputs.insert(input);
151 Ok(())
152 }
153
154 #[inline]
160 pub fn add_instruction(&mut self, instruction: Instruction<N>) -> Result<()> {
161 ensure!(self.outputs.is_empty(), "Cannot add instructions after outputs have been added");
163
164 ensure!(
166 self.instructions.len() < N::MAX_INSTRUCTIONS,
167 "Cannot add more than {} instructions",
168 N::MAX_INSTRUCTIONS
169 );
170
171 for register in instruction.destinations() {
173 ensure!(matches!(register, Register::Locator(..)), "Destination register must be a locator");
174 }
175
176 self.instructions.push(instruction);
178 Ok(())
179 }
180
181 #[inline]
186 fn add_output(&mut self, output: Output<N>) -> Result<()> {
187 ensure!(self.outputs.len() < N::MAX_OUTPUTS, "Cannot add more than {} outputs", N::MAX_OUTPUTS);
189
190 ensure!(!matches!(output.register_type(), RegisterType::Record(..)), "Closure outputs do not support records");
193
194 self.outputs.insert(output);
196 Ok(())
197 }
198}
199
200impl<N: Network> TypeName for ClosureCore<N> {
201 #[inline]
203 fn type_name() -> &'static str {
204 "closure"
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 use crate::{Closure, Instruction};
213
214 type CurrentNetwork = console::network::MainnetV0;
215
216 #[test]
217 fn test_add_input() {
218 let name = Identifier::from_str("closure_core_test").unwrap();
220 let mut closure = Closure::<CurrentNetwork>::new(name);
221
222 let input = Input::<CurrentNetwork>::from_str("input r0 as field;").unwrap();
224 assert!(closure.add_input(input.clone()).is_ok());
225
226 assert!(closure.add_input(input).is_err());
228
229 for i in 1..CurrentNetwork::MAX_INPUTS * 2 {
231 let input = Input::<CurrentNetwork>::from_str(&format!("input r{i} as field;")).unwrap();
232
233 match closure.inputs.len() < CurrentNetwork::MAX_INPUTS {
234 true => assert!(closure.add_input(input).is_ok()),
235 false => assert!(closure.add_input(input).is_err()),
236 }
237 }
238 }
239
240 #[test]
241 fn test_add_instruction() {
242 let name = Identifier::from_str("closure_core_test").unwrap();
244 let mut closure = Closure::<CurrentNetwork>::new(name);
245
246 let instruction = Instruction::<CurrentNetwork>::from_str("add r0 r1 into r2;").unwrap();
248 assert!(closure.add_instruction(instruction).is_ok());
249
250 for i in 3..CurrentNetwork::MAX_INSTRUCTIONS * 2 {
252 let instruction = Instruction::<CurrentNetwork>::from_str(&format!("add r0 r1 into r{i};")).unwrap();
253
254 match closure.instructions.len() < CurrentNetwork::MAX_INSTRUCTIONS {
255 true => assert!(closure.add_instruction(instruction).is_ok()),
256 false => assert!(closure.add_instruction(instruction).is_err()),
257 }
258 }
259 }
260
261 #[test]
262 fn test_add_output() {
263 let name = Identifier::from_str("closure_core_test").unwrap();
265 let mut closure = Closure::<CurrentNetwork>::new(name);
266
267 let output = Output::<CurrentNetwork>::from_str("output r0 as field;").unwrap();
269 assert!(closure.add_output(output).is_ok());
270
271 for i in 1..CurrentNetwork::MAX_OUTPUTS * 2 {
273 let output = Output::<CurrentNetwork>::from_str(&format!("output r{i} as field;")).unwrap();
274
275 match closure.outputs.len() < CurrentNetwork::MAX_OUTPUTS {
276 true => assert!(closure.add_output(output).is_ok()),
277 false => assert!(closure.add_output(output).is_err()),
278 }
279 }
280 }
281
282 #[test]
283 fn test_add_output_record_types_rejected() {
284 let name = Identifier::from_str("closure_core_test").unwrap();
285
286 let mut closure = Closure::<CurrentNetwork>::new(name);
288 let output = Output::<CurrentNetwork>::from_str("output r0 as dynamic.record;").unwrap();
289 assert!(closure.add_output(output).is_ok());
290
291 let mut closure = Closure::<CurrentNetwork>::new(name);
293 let output = Output::<CurrentNetwork>::from_str("output r0 as test_prog.aleo/token.record;").unwrap();
294 assert!(closure.add_output(output).is_ok());
295
296 let mut closure = Closure::<CurrentNetwork>::new(name);
298 let output = Output::<CurrentNetwork>::from_str("output r0 as token.record;").unwrap();
299 let result = closure.add_output(output);
300 assert!(result.is_err());
301 assert!(result.unwrap_err().to_string().contains("Closure outputs do not support records"));
302 }
303}