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 types::U8,
30};
31
32use indexmap::IndexSet;
33
34#[derive(Clone, PartialEq, Eq)]
35pub struct FunctionCore<N: Network> {
36 name: Identifier<N>,
38 inputs: IndexSet<Input<N>>,
41 instructions: Vec<Instruction<N>>,
43 outputs: IndexSet<Output<N>>,
45 finalize_logic: Option<FinalizeCore<N>>,
47}
48
49impl<N: Network> FunctionCore<N> {
50 pub fn new(name: Identifier<N>) -> Self {
52 Self { name, inputs: IndexSet::new(), instructions: Vec::new(), outputs: IndexSet::new(), finalize_logic: None }
53 }
54
55 pub const fn name(&self) -> &Identifier<N> {
57 &self.name
58 }
59
60 pub const fn inputs(&self) -> &IndexSet<Input<N>> {
62 &self.inputs
63 }
64
65 pub fn input_types(&self) -> Vec<ValueType<N>> {
67 self.inputs.iter().map(|input| input.value_type()).cloned().collect()
68 }
69
70 pub fn instructions(&self) -> &[Instruction<N>] {
72 &self.instructions
73 }
74
75 pub const fn outputs(&self) -> &IndexSet<Output<N>> {
77 &self.outputs
78 }
79
80 pub fn output_types(&self) -> Vec<ValueType<N>> {
82 self.outputs.iter().map(|output| output.value_type()).cloned().collect()
83 }
84
85 pub const fn finalize_logic(&self) -> Option<&FinalizeCore<N>> {
87 self.finalize_logic.as_ref()
88 }
89
90 pub fn contains_external_struct(&self) -> bool {
92 self.inputs.iter().any(|input| input.value_type().contains_external_struct())
93 || self.outputs.iter().any(|output| output.value_type().contains_external_struct())
94 || self.instructions.iter().any(|instruction| instruction.contains_external_struct())
95 || self.finalize_logic.iter().any(|finalize| finalize.contains_external_struct())
96 }
97
98 pub fn contains_string_type(&self) -> bool {
100 self.input_types().iter().any(|input| input.contains_string_type())
101 || self.output_types().iter().any(|output| output.contains_string_type())
102 || self.instructions.iter().any(|instruction| instruction.contains_string_type())
103 || self.finalize_logic.as_ref().map(|finalize| finalize.contains_string_type()).unwrap_or(false)
104 }
105
106 pub fn contains_identifier_type(&self) -> Result<bool> {
108 for input in self.input_types() {
109 if input.contains_identifier_type()? {
110 return Ok(true);
111 }
112 }
113 for output in self.output_types() {
114 if output.contains_identifier_type()? {
115 return Ok(true);
116 }
117 }
118 for instruction in &self.instructions {
120 if instruction.contains_identifier_type()? {
121 return Ok(true);
122 }
123 }
124 if let Some(finalize) = &self.finalize_logic {
125 if finalize.contains_identifier_type()? {
126 return Ok(true);
127 }
128 }
129 Ok(false)
130 }
131
132 pub fn exceeds_max_array_size(&self, max_array_size: u32) -> bool {
134 self.inputs.iter().any(|input| input.value_type().exceeds_max_array_size(max_array_size))
135 || self.outputs.iter().any(|output| output.value_type().exceeds_max_array_size(max_array_size))
136 || self.instructions.iter().any(|instruction| instruction.exceeds_max_array_size(max_array_size))
137 || self.finalize_logic.iter().any(|finalize| finalize.exceeds_max_array_size(max_array_size))
138 }
139}
140
141impl<N: Network> FunctionCore<N> {
142 #[inline]
150 fn add_input(&mut self, input: Input<N>) -> Result<()> {
151 ensure!(self.instructions.is_empty(), "Cannot add inputs after instructions have been added");
153 ensure!(self.outputs.is_empty(), "Cannot add inputs after outputs have been added");
154
155 ensure!(self.inputs.len() < N::MAX_INPUTS, "Cannot add more than {} inputs", N::MAX_INPUTS);
157 ensure!(!self.inputs.contains(&input), "Cannot add duplicate input statement");
159
160 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
162
163 ensure!(matches!(input.register(), Register::Locator(..)), "Input register must be a locator");
165
166 self.inputs.insert(input);
168 Ok(())
169 }
170
171 #[inline]
178 pub fn add_instruction(&mut self, instruction: Instruction<N>) -> Result<()> {
179 ensure!(self.outputs.is_empty(), "Cannot add instructions after outputs have been added");
181
182 ensure!(
184 self.instructions.len() < N::MAX_INSTRUCTIONS,
185 "Cannot add more than {} instructions",
186 N::MAX_INSTRUCTIONS
187 );
188
189 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
191
192 for register in instruction.destinations() {
194 ensure!(matches!(register, Register::Locator(..)), "Destination register must be a locator");
195 }
196
197 self.instructions.push(instruction);
199 Ok(())
200 }
201
202 #[inline]
208 fn add_output(&mut self, output: Output<N>) -> Result<()> {
209 ensure!(self.outputs.len() < N::MAX_OUTPUTS, "Cannot add more than {} outputs", N::MAX_OUTPUTS);
211 ensure!(!self.outputs.contains(&output), "Cannot add duplicate output statement");
213
214 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
216
217 self.outputs.insert(output);
219 Ok(())
220 }
221
222 #[inline]
230 fn add_finalize(&mut self, finalize: FinalizeCore<N>) -> Result<()> {
231 ensure!(self.finalize_logic.is_none(), "Cannot add multiple finalize scopes to function '{}'", self.name);
233 ensure!(*finalize.name() == self.name, "Finalize scope name must match function name '{}'", self.name);
235 ensure!(finalize.inputs().len() <= N::MAX_INPUTS, "Cannot add more than {} inputs to finalize", N::MAX_INPUTS);
237
238 self.finalize_logic = Some(finalize);
240 Ok(())
241 }
242
243 pub fn to_checksum(&self) -> [U8<N>; 32] {
248 crate::to_checksum::source_code_checksum(&self.to_string())
249 }
250}
251
252impl<N: Network> TypeName for FunctionCore<N> {
253 #[inline]
255 fn type_name() -> &'static str {
256 "function"
257 }
258}
259
260#[cfg(test)]
261mod tests {
262 use super::*;
263
264 use crate::{Function, Instruction};
265
266 type CurrentNetwork = console::network::MainnetV0;
267
268 #[test]
269 fn test_add_input() {
270 let name = Identifier::from_str("function_core_test").unwrap();
272 let mut function = Function::<CurrentNetwork>::new(name);
273
274 let input = Input::<CurrentNetwork>::from_str("input r0 as field.private;").unwrap();
276 assert!(function.add_input(input.clone()).is_ok());
277
278 assert!(function.add_input(input).is_err());
280
281 for i in 1..CurrentNetwork::MAX_INPUTS * 2 {
283 let input = Input::<CurrentNetwork>::from_str(&format!("input r{i} as field.private;")).unwrap();
284
285 match function.inputs.len() < CurrentNetwork::MAX_INPUTS {
286 true => assert!(function.add_input(input).is_ok()),
287 false => assert!(function.add_input(input).is_err()),
288 }
289 }
290 }
291
292 #[test]
293 fn test_add_instruction() {
294 let name = Identifier::from_str("function_core_test").unwrap();
296 let mut function = Function::<CurrentNetwork>::new(name);
297
298 let instruction = Instruction::<CurrentNetwork>::from_str("add r0 r1 into r2;").unwrap();
300 assert!(function.add_instruction(instruction).is_ok());
301
302 for i in 3..CurrentNetwork::MAX_INSTRUCTIONS * 2 {
304 let instruction = Instruction::<CurrentNetwork>::from_str(&format!("add r0 r1 into r{i};")).unwrap();
305
306 match function.instructions.len() < CurrentNetwork::MAX_INSTRUCTIONS {
307 true => assert!(function.add_instruction(instruction).is_ok()),
308 false => assert!(function.add_instruction(instruction).is_err()),
309 }
310 }
311 }
312
313 #[test]
314 fn test_add_output() {
315 let name = Identifier::from_str("function_core_test").unwrap();
317 let mut function = Function::<CurrentNetwork>::new(name);
318
319 let output = Output::<CurrentNetwork>::from_str("output r0 as field.private;").unwrap();
321 assert!(function.add_output(output).is_ok());
322
323 for i in 1..CurrentNetwork::MAX_OUTPUTS * 2 {
325 let output = Output::<CurrentNetwork>::from_str(&format!("output r{i} as field.private;")).unwrap();
326
327 match function.outputs.len() < CurrentNetwork::MAX_OUTPUTS {
328 true => assert!(function.add_output(output).is_ok()),
329 false => assert!(function.add_output(output).is_err()),
330 }
331 }
332 }
333}