snarkvm_synthesizer_program/function/
mod.rs1mod input;
17use input::*;
18
19mod output;
20use output::*;
21
22mod bytes;
23mod parse;
24
25use crate::{Instruction, finalize::FinalizeCore};
26use console::{
27 network::prelude::*,
28 program::{Identifier, Register, ValueType},
29};
30
31use indexmap::IndexSet;
32
33#[derive(Clone, PartialEq, Eq)]
34pub struct FunctionCore<N: Network> {
35 name: Identifier<N>,
37 inputs: IndexSet<Input<N>>,
40 instructions: Vec<Instruction<N>>,
42 outputs: IndexSet<Output<N>>,
44 finalize_logic: Option<FinalizeCore<N>>,
46}
47
48impl<N: Network> FunctionCore<N> {
49 pub fn new(name: Identifier<N>) -> Self {
51 Self { name, inputs: IndexSet::new(), instructions: Vec::new(), outputs: IndexSet::new(), finalize_logic: None }
52 }
53
54 pub const fn name(&self) -> &Identifier<N> {
56 &self.name
57 }
58
59 pub const fn inputs(&self) -> &IndexSet<Input<N>> {
61 &self.inputs
62 }
63
64 pub fn input_types(&self) -> Vec<ValueType<N>> {
66 self.inputs.iter().map(|input| input.value_type()).cloned().collect()
67 }
68
69 pub fn instructions(&self) -> &[Instruction<N>] {
71 &self.instructions
72 }
73
74 pub const fn outputs(&self) -> &IndexSet<Output<N>> {
76 &self.outputs
77 }
78
79 pub fn output_types(&self) -> Vec<ValueType<N>> {
81 self.outputs.iter().map(|output| output.value_type()).cloned().collect()
82 }
83
84 pub const fn finalize_logic(&self) -> Option<&FinalizeCore<N>> {
86 self.finalize_logic.as_ref()
87 }
88
89 pub fn contains_external_struct(&self) -> bool {
91 self.inputs.iter().any(|input| input.value_type().contains_external_struct())
92 || self.outputs.iter().any(|output| output.value_type().contains_external_struct())
93 || self.instructions.iter().any(|instruction| instruction.contains_external_struct())
94 || self.finalize_logic.iter().any(|finalize| finalize.contains_external_struct())
95 }
96
97 pub fn contains_string_type(&self) -> bool {
99 self.input_types().iter().any(|input| input.contains_string_type())
100 || self.output_types().iter().any(|output| output.contains_string_type())
101 || self.instructions.iter().any(|instruction| instruction.contains_string_type())
102 || self.finalize_logic.as_ref().map(|finalize| finalize.contains_string_type()).unwrap_or(false)
103 }
104
105 pub fn contains_identifier_type(&self) -> Result<bool> {
107 for input in self.input_types() {
108 if input.contains_identifier_type()? {
109 return Ok(true);
110 }
111 }
112 for output in self.output_types() {
113 if output.contains_identifier_type()? {
114 return Ok(true);
115 }
116 }
117 for instruction in &self.instructions {
119 if instruction.contains_identifier_type()? {
120 return Ok(true);
121 }
122 }
123 if let Some(finalize) = &self.finalize_logic {
124 if finalize.contains_identifier_type()? {
125 return Ok(true);
126 }
127 }
128 Ok(false)
129 }
130
131 pub fn exceeds_max_array_size(&self, max_array_size: u32) -> bool {
133 self.inputs.iter().any(|input| input.value_type().exceeds_max_array_size(max_array_size))
134 || self.outputs.iter().any(|output| output.value_type().exceeds_max_array_size(max_array_size))
135 || self.instructions.iter().any(|instruction| instruction.exceeds_max_array_size(max_array_size))
136 || self.finalize_logic.iter().any(|finalize| finalize.exceeds_max_array_size(max_array_size))
137 }
138}
139
140impl<N: Network> FunctionCore<N> {
141 #[inline]
149 fn add_input(&mut self, input: Input<N>) -> Result<()> {
150 ensure!(self.instructions.is_empty(), "Cannot add inputs after instructions have been added");
152 ensure!(self.outputs.is_empty(), "Cannot add inputs after outputs have been added");
153
154 ensure!(self.inputs.len() < N::MAX_INPUTS, "Cannot add more than {} inputs", N::MAX_INPUTS);
156 ensure!(!self.inputs.contains(&input), "Cannot add duplicate input statement");
158
159 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
161
162 ensure!(matches!(input.register(), Register::Locator(..)), "Input register must be a locator");
164
165 self.inputs.insert(input);
167 Ok(())
168 }
169
170 #[inline]
177 pub fn add_instruction(&mut self, instruction: Instruction<N>) -> Result<()> {
178 ensure!(self.outputs.is_empty(), "Cannot add instructions after outputs have been added");
180
181 ensure!(
183 self.instructions.len() < N::MAX_INSTRUCTIONS,
184 "Cannot add more than {} instructions",
185 N::MAX_INSTRUCTIONS
186 );
187
188 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
190
191 for register in instruction.destinations() {
193 ensure!(matches!(register, Register::Locator(..)), "Destination register must be a locator");
194 }
195
196 self.instructions.push(instruction);
198 Ok(())
199 }
200
201 #[inline]
207 fn add_output(&mut self, output: Output<N>) -> Result<()> {
208 ensure!(self.outputs.len() < N::MAX_OUTPUTS, "Cannot add more than {} outputs", N::MAX_OUTPUTS);
210 ensure!(!self.outputs.contains(&output), "Cannot add duplicate output statement");
212
213 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
215
216 self.outputs.insert(output);
218 Ok(())
219 }
220
221 #[inline]
229 fn add_finalize(&mut self, finalize: FinalizeCore<N>) -> Result<()> {
230 ensure!(self.finalize_logic.is_none(), "Cannot add multiple finalize scopes to function '{}'", self.name);
232 ensure!(*finalize.name() == self.name, "Finalize scope name must match function name '{}'", self.name);
234 ensure!(finalize.inputs().len() <= N::MAX_INPUTS, "Cannot add more than {} inputs to finalize", N::MAX_INPUTS);
236
237 self.finalize_logic = Some(finalize);
239 Ok(())
240 }
241}
242
243impl<N: Network> TypeName for FunctionCore<N> {
244 #[inline]
246 fn type_name() -> &'static str {
247 "function"
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254
255 use crate::{Function, Instruction};
256
257 type CurrentNetwork = console::network::MainnetV0;
258
259 #[test]
260 fn test_add_input() {
261 let name = Identifier::from_str("function_core_test").unwrap();
263 let mut function = Function::<CurrentNetwork>::new(name);
264
265 let input = Input::<CurrentNetwork>::from_str("input r0 as field.private;").unwrap();
267 assert!(function.add_input(input.clone()).is_ok());
268
269 assert!(function.add_input(input).is_err());
271
272 for i in 1..CurrentNetwork::MAX_INPUTS * 2 {
274 let input = Input::<CurrentNetwork>::from_str(&format!("input r{i} as field.private;")).unwrap();
275
276 match function.inputs.len() < CurrentNetwork::MAX_INPUTS {
277 true => assert!(function.add_input(input).is_ok()),
278 false => assert!(function.add_input(input).is_err()),
279 }
280 }
281 }
282
283 #[test]
284 fn test_add_instruction() {
285 let name = Identifier::from_str("function_core_test").unwrap();
287 let mut function = Function::<CurrentNetwork>::new(name);
288
289 let instruction = Instruction::<CurrentNetwork>::from_str("add r0 r1 into r2;").unwrap();
291 assert!(function.add_instruction(instruction).is_ok());
292
293 for i in 3..CurrentNetwork::MAX_INSTRUCTIONS * 2 {
295 let instruction = Instruction::<CurrentNetwork>::from_str(&format!("add r0 r1 into r{i};")).unwrap();
296
297 match function.instructions.len() < CurrentNetwork::MAX_INSTRUCTIONS {
298 true => assert!(function.add_instruction(instruction).is_ok()),
299 false => assert!(function.add_instruction(instruction).is_err()),
300 }
301 }
302 }
303
304 #[test]
305 fn test_add_output() {
306 let name = Identifier::from_str("function_core_test").unwrap();
308 let mut function = Function::<CurrentNetwork>::new(name);
309
310 let output = Output::<CurrentNetwork>::from_str("output r0 as field.private;").unwrap();
312 assert!(function.add_output(output).is_ok());
313
314 for i in 1..CurrentNetwork::MAX_OUTPUTS * 2 {
316 let output = Output::<CurrentNetwork>::from_str(&format!("output r{i} as field.private;")).unwrap();
317
318 match function.outputs.len() < CurrentNetwork::MAX_OUTPUTS {
319 true => assert!(function.add_output(output).is_ok()),
320 false => assert!(function.add_output(output).is_err()),
321 }
322 }
323 }
324}