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_external_struct(&self) -> bool {
101 self.inputs.iter().any(|input| input.value_type().contains_external_struct())
102 || self.outputs.iter().any(|output| output.value_type().contains_external_struct())
103 || self.instructions.iter().any(|instruction| instruction.contains_external_struct())
104 || self.finalize_logic.iter().any(|finalize| finalize.contains_external_struct())
105 }
106
107 pub fn contains_string_type(&self) -> bool {
109 self.input_types().iter().any(|input| input.contains_string_type())
110 || self.output_types().iter().any(|output| output.contains_string_type())
111 || self.instructions.iter().any(|instruction| instruction.contains_string_type())
112 || self.finalize_logic.as_ref().map(|finalize| finalize.contains_string_type()).unwrap_or(false)
113 }
114
115 pub fn exceeds_max_array_size(&self, max_array_size: u32) -> bool {
117 self.inputs.iter().any(|input| input.value_type().exceeds_max_array_size(max_array_size))
118 || self.outputs.iter().any(|output| output.value_type().exceeds_max_array_size(max_array_size))
119 || self.instructions.iter().any(|instruction| instruction.exceeds_max_array_size(max_array_size))
120 || self.finalize_logic.iter().any(|finalize| finalize.exceeds_max_array_size(max_array_size))
121 }
122}
123
124impl<N: Network> FunctionCore<N> {
125 #[inline]
133 fn add_input(&mut self, input: Input<N>) -> Result<()> {
134 ensure!(self.instructions.is_empty(), "Cannot add inputs after instructions have been added");
136 ensure!(self.outputs.is_empty(), "Cannot add inputs after outputs have been added");
137
138 ensure!(self.inputs.len() < N::MAX_INPUTS, "Cannot add more than {} inputs", N::MAX_INPUTS);
140 ensure!(!self.inputs.contains(&input), "Cannot add duplicate input statement");
142
143 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
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]
161 pub fn add_instruction(&mut self, instruction: Instruction<N>) -> Result<()> {
162 ensure!(self.outputs.is_empty(), "Cannot add instructions after outputs have been added");
164
165 ensure!(
167 self.instructions.len() < N::MAX_INSTRUCTIONS,
168 "Cannot add more than {} instructions",
169 N::MAX_INSTRUCTIONS
170 );
171
172 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
174
175 for register in instruction.destinations() {
177 ensure!(matches!(register, Register::Locator(..)), "Destination register must be a locator");
178 }
179
180 self.instructions.push(instruction);
182 Ok(())
183 }
184
185 #[inline]
191 fn add_output(&mut self, output: Output<N>) -> Result<()> {
192 ensure!(self.outputs.len() < N::MAX_OUTPUTS, "Cannot add more than {} outputs", N::MAX_OUTPUTS);
194 ensure!(!self.outputs.contains(&output), "Cannot add duplicate output statement");
196
197 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
199
200 self.outputs.insert(output);
202 Ok(())
203 }
204
205 #[inline]
213 fn add_finalize(&mut self, finalize: FinalizeCore<N>) -> Result<()> {
214 ensure!(self.finalize_logic.is_none(), "Cannot add multiple finalize scopes to function '{}'", self.name);
216 ensure!(*finalize.name() == self.name, "Finalize scope name must match function name '{}'", self.name);
218 ensure!(finalize.inputs().len() <= N::MAX_INPUTS, "Cannot add more than {} inputs to finalize", N::MAX_INPUTS);
220
221 self.finalize_logic = Some(finalize);
223 Ok(())
224 }
225}
226
227impl<N: Network> TypeName for FunctionCore<N> {
228 #[inline]
230 fn type_name() -> &'static str {
231 "function"
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use super::*;
238
239 use crate::{Function, Instruction};
240
241 type CurrentNetwork = console::network::MainnetV0;
242
243 #[test]
244 fn test_add_input() {
245 let name = Identifier::from_str("function_core_test").unwrap();
247 let mut function = Function::<CurrentNetwork>::new(name);
248
249 let input = Input::<CurrentNetwork>::from_str("input r0 as field.private;").unwrap();
251 assert!(function.add_input(input.clone()).is_ok());
252
253 assert!(function.add_input(input).is_err());
255
256 for i in 1..CurrentNetwork::MAX_INPUTS * 2 {
258 let input = Input::<CurrentNetwork>::from_str(&format!("input r{i} as field.private;")).unwrap();
259
260 match function.inputs.len() < CurrentNetwork::MAX_INPUTS {
261 true => assert!(function.add_input(input).is_ok()),
262 false => assert!(function.add_input(input).is_err()),
263 }
264 }
265 }
266
267 #[test]
268 fn test_add_instruction() {
269 let name = Identifier::from_str("function_core_test").unwrap();
271 let mut function = Function::<CurrentNetwork>::new(name);
272
273 let instruction = Instruction::<CurrentNetwork>::from_str("add r0 r1 into r2;").unwrap();
275 assert!(function.add_instruction(instruction).is_ok());
276
277 for i in 3..CurrentNetwork::MAX_INSTRUCTIONS * 2 {
279 let instruction = Instruction::<CurrentNetwork>::from_str(&format!("add r0 r1 into r{i};")).unwrap();
280
281 match function.instructions.len() < CurrentNetwork::MAX_INSTRUCTIONS {
282 true => assert!(function.add_instruction(instruction).is_ok()),
283 false => assert!(function.add_instruction(instruction).is_err()),
284 }
285 }
286 }
287
288 #[test]
289 fn test_add_output() {
290 let name = Identifier::from_str("function_core_test").unwrap();
292 let mut function = Function::<CurrentNetwork>::new(name);
293
294 let output = Output::<CurrentNetwork>::from_str("output r0 as field.private;").unwrap();
296 assert!(function.add_output(output).is_ok());
297
298 for i in 1..CurrentNetwork::MAX_OUTPUTS * 2 {
300 let output = Output::<CurrentNetwork>::from_str(&format!("output r{i} as field.private;")).unwrap();
301
302 match function.outputs.len() < CurrentNetwork::MAX_OUTPUTS {
303 true => assert!(function.add_output(output).is_ok()),
304 false => assert!(function.add_output(output).is_err()),
305 }
306 }
307 }
308}