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, Variant},
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 input_variants(&self) -> Vec<Variant> {
71 self.inputs.iter().map(|input| input.value_type().variant()).collect()
72 }
73
74 pub fn instructions(&self) -> &[Instruction<N>] {
76 &self.instructions
77 }
78
79 pub const fn outputs(&self) -> &IndexSet<Output<N>> {
81 &self.outputs
82 }
83
84 pub fn output_types(&self) -> Vec<ValueType<N>> {
86 self.outputs.iter().map(|output| output.value_type()).cloned().collect()
87 }
88
89 pub fn output_variants(&self) -> Vec<Variant> {
91 self.outputs.iter().map(|output| output.value_type().variant()).collect()
92 }
93
94 pub const fn finalize_logic(&self) -> Option<&FinalizeCore<N>> {
96 self.finalize_logic.as_ref()
97 }
98
99 pub fn contains_string_type(&self) -> bool {
101 self.input_types().iter().any(|input| input.contains_string_type())
102 || self.output_types().iter().any(|output| output.contains_string_type())
103 || self.instructions.iter().any(|instruction| instruction.contains_string_type())
104 || self.finalize_logic.as_ref().map(|finalize| finalize.contains_string_type()).unwrap_or(false)
105 }
106
107 pub fn exceeds_max_array_size(&self, max_array_size: u32) -> bool {
109 self.inputs.iter().any(|input| input.value_type().exceeds_max_array_size(max_array_size))
110 || self.outputs.iter().any(|output| output.value_type().exceeds_max_array_size(max_array_size))
111 || self.instructions.iter().any(|instruction| instruction.exceeds_max_array_size(max_array_size))
112 || self.finalize_logic.iter().any(|finalize| finalize.exceeds_max_array_size(max_array_size))
113 }
114}
115
116impl<N: Network> FunctionCore<N> {
117 #[inline]
125 fn add_input(&mut self, input: Input<N>) -> Result<()> {
126 ensure!(self.instructions.is_empty(), "Cannot add inputs after instructions have been added");
128 ensure!(self.outputs.is_empty(), "Cannot add inputs after outputs have been added");
129
130 ensure!(self.inputs.len() < N::MAX_INPUTS, "Cannot add more than {} inputs", N::MAX_INPUTS);
132 ensure!(!self.inputs.contains(&input), "Cannot add duplicate input statement");
134
135 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
137
138 ensure!(matches!(input.register(), Register::Locator(..)), "Input register must be a locator");
140
141 self.inputs.insert(input);
143 Ok(())
144 }
145
146 #[inline]
153 pub fn add_instruction(&mut self, instruction: Instruction<N>) -> Result<()> {
154 ensure!(self.outputs.is_empty(), "Cannot add instructions after outputs have been added");
156
157 ensure!(
159 self.instructions.len() < N::MAX_INSTRUCTIONS,
160 "Cannot add more than {} instructions",
161 N::MAX_INSTRUCTIONS
162 );
163
164 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
166
167 for register in instruction.destinations() {
169 ensure!(matches!(register, Register::Locator(..)), "Destination register must be a locator");
170 }
171
172 self.instructions.push(instruction);
174 Ok(())
175 }
176
177 #[inline]
183 fn add_output(&mut self, output: Output<N>) -> Result<()> {
184 ensure!(self.outputs.len() < N::MAX_OUTPUTS, "Cannot add more than {} outputs", N::MAX_OUTPUTS);
186 ensure!(!self.outputs.contains(&output), "Cannot add duplicate output statement");
188
189 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
191
192 self.outputs.insert(output);
194 Ok(())
195 }
196
197 #[inline]
205 fn add_finalize(&mut self, finalize: FinalizeCore<N>) -> Result<()> {
206 ensure!(self.finalize_logic.is_none(), "Cannot add multiple finalize scopes to function '{}'", self.name);
208 ensure!(*finalize.name() == self.name, "Finalize scope name must match function name '{}'", self.name);
210 ensure!(finalize.inputs().len() <= N::MAX_INPUTS, "Cannot add more than {} inputs to finalize", N::MAX_INPUTS);
212
213 self.finalize_logic = Some(finalize);
215 Ok(())
216 }
217}
218
219impl<N: Network> TypeName for FunctionCore<N> {
220 #[inline]
222 fn type_name() -> &'static str {
223 "function"
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230
231 use crate::{Function, Instruction};
232
233 type CurrentNetwork = console::network::MainnetV0;
234
235 #[test]
236 fn test_add_input() {
237 let name = Identifier::from_str("function_core_test").unwrap();
239 let mut function = Function::<CurrentNetwork>::new(name);
240
241 let input = Input::<CurrentNetwork>::from_str("input r0 as field.private;").unwrap();
243 assert!(function.add_input(input.clone()).is_ok());
244
245 assert!(function.add_input(input).is_err());
247
248 for i in 1..CurrentNetwork::MAX_INPUTS * 2 {
250 let input = Input::<CurrentNetwork>::from_str(&format!("input r{i} as field.private;")).unwrap();
251
252 match function.inputs.len() < CurrentNetwork::MAX_INPUTS {
253 true => assert!(function.add_input(input).is_ok()),
254 false => assert!(function.add_input(input).is_err()),
255 }
256 }
257 }
258
259 #[test]
260 fn test_add_instruction() {
261 let name = Identifier::from_str("function_core_test").unwrap();
263 let mut function = Function::<CurrentNetwork>::new(name);
264
265 let instruction = Instruction::<CurrentNetwork>::from_str("add r0 r1 into r2;").unwrap();
267 assert!(function.add_instruction(instruction).is_ok());
268
269 for i in 3..CurrentNetwork::MAX_INSTRUCTIONS * 2 {
271 let instruction = Instruction::<CurrentNetwork>::from_str(&format!("add r0 r1 into r{i};")).unwrap();
272
273 match function.instructions.len() < CurrentNetwork::MAX_INSTRUCTIONS {
274 true => assert!(function.add_instruction(instruction).is_ok()),
275 false => assert!(function.add_instruction(instruction).is_err()),
276 }
277 }
278 }
279
280 #[test]
281 fn test_add_output() {
282 let name = Identifier::from_str("function_core_test").unwrap();
284 let mut function = Function::<CurrentNetwork>::new(name);
285
286 let output = Output::<CurrentNetwork>::from_str("output r0 as field.private;").unwrap();
288 assert!(function.add_output(output).is_ok());
289
290 for i in 1..CurrentNetwork::MAX_OUTPUTS * 2 {
292 let output = Output::<CurrentNetwork>::from_str(&format!("output r{i} as field.private;")).unwrap();
293
294 match function.outputs.len() < CurrentNetwork::MAX_OUTPUTS {
295 true => assert!(function.add_output(output).is_ok()),
296 false => assert!(function.add_output(output).is_err()),
297 }
298 }
299 }
300}