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 exceeds_max_array_size(&self, max_array_size: u32) -> bool {
101 self.inputs.iter().any(|input| input.value_type().exceeds_max_array_size(max_array_size))
102 || self.outputs.iter().any(|output| output.value_type().exceeds_max_array_size(max_array_size))
103 || self.instructions.iter().any(|instruction| instruction.exceeds_max_array_size(max_array_size))
104 || self.finalize_logic.iter().any(|finalize| finalize.exceeds_max_array_size(max_array_size))
105 }
106}
107
108impl<N: Network> FunctionCore<N> {
109 #[inline]
117 fn add_input(&mut self, input: Input<N>) -> Result<()> {
118 ensure!(self.instructions.is_empty(), "Cannot add inputs after instructions have been added");
120 ensure!(self.outputs.is_empty(), "Cannot add inputs after outputs have been added");
121
122 ensure!(self.inputs.len() < N::MAX_INPUTS, "Cannot add more than {} inputs", N::MAX_INPUTS);
124 ensure!(!self.inputs.contains(&input), "Cannot add duplicate input statement");
126
127 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
129
130 ensure!(matches!(input.register(), Register::Locator(..)), "Input register must be a locator");
132
133 self.inputs.insert(input);
135 Ok(())
136 }
137
138 #[inline]
145 pub fn add_instruction(&mut self, instruction: Instruction<N>) -> Result<()> {
146 ensure!(self.outputs.is_empty(), "Cannot add instructions after outputs have been added");
148
149 ensure!(
151 self.instructions.len() < N::MAX_INSTRUCTIONS,
152 "Cannot add more than {} instructions",
153 N::MAX_INSTRUCTIONS
154 );
155
156 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
158
159 for register in instruction.destinations() {
161 ensure!(matches!(register, Register::Locator(..)), "Destination register must be a locator");
162 }
163
164 self.instructions.push(instruction);
166 Ok(())
167 }
168
169 #[inline]
175 fn add_output(&mut self, output: Output<N>) -> Result<()> {
176 ensure!(self.outputs.len() < N::MAX_OUTPUTS, "Cannot add more than {} outputs", N::MAX_OUTPUTS);
178 ensure!(!self.outputs.contains(&output), "Cannot add duplicate output statement");
180
181 ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
183
184 self.outputs.insert(output);
186 Ok(())
187 }
188
189 #[inline]
197 fn add_finalize(&mut self, finalize: FinalizeCore<N>) -> Result<()> {
198 ensure!(self.finalize_logic.is_none(), "Cannot add multiple finalize scopes to function '{}'", self.name);
200 ensure!(*finalize.name() == self.name, "Finalize scope name must match function name '{}'", self.name);
202 ensure!(finalize.inputs().len() <= N::MAX_INPUTS, "Cannot add more than {} inputs to finalize", N::MAX_INPUTS);
204
205 self.finalize_logic = Some(finalize);
207 Ok(())
208 }
209}
210
211impl<N: Network> TypeName for FunctionCore<N> {
212 #[inline]
214 fn type_name() -> &'static str {
215 "function"
216 }
217}
218
219#[cfg(test)]
220mod tests {
221 use super::*;
222
223 use crate::{Function, Instruction};
224
225 type CurrentNetwork = console::network::MainnetV0;
226
227 #[test]
228 fn test_add_input() {
229 let name = Identifier::from_str("function_core_test").unwrap();
231 let mut function = Function::<CurrentNetwork>::new(name);
232
233 let input = Input::<CurrentNetwork>::from_str("input r0 as field.private;").unwrap();
235 assert!(function.add_input(input.clone()).is_ok());
236
237 assert!(function.add_input(input).is_err());
239
240 for i in 1..CurrentNetwork::MAX_INPUTS * 2 {
242 let input = Input::<CurrentNetwork>::from_str(&format!("input r{i} as field.private;")).unwrap();
243
244 match function.inputs.len() < CurrentNetwork::MAX_INPUTS {
245 true => assert!(function.add_input(input).is_ok()),
246 false => assert!(function.add_input(input).is_err()),
247 }
248 }
249 }
250
251 #[test]
252 fn test_add_instruction() {
253 let name = Identifier::from_str("function_core_test").unwrap();
255 let mut function = Function::<CurrentNetwork>::new(name);
256
257 let instruction = Instruction::<CurrentNetwork>::from_str("add r0 r1 into r2;").unwrap();
259 assert!(function.add_instruction(instruction).is_ok());
260
261 for i in 3..CurrentNetwork::MAX_INSTRUCTIONS * 2 {
263 let instruction = Instruction::<CurrentNetwork>::from_str(&format!("add r0 r1 into r{i};")).unwrap();
264
265 match function.instructions.len() < CurrentNetwork::MAX_INSTRUCTIONS {
266 true => assert!(function.add_instruction(instruction).is_ok()),
267 false => assert!(function.add_instruction(instruction).is_err()),
268 }
269 }
270 }
271
272 #[test]
273 fn test_add_output() {
274 let name = Identifier::from_str("function_core_test").unwrap();
276 let mut function = Function::<CurrentNetwork>::new(name);
277
278 let output = Output::<CurrentNetwork>::from_str("output r0 as field.private;").unwrap();
280 assert!(function.add_output(output).is_ok());
281
282 for i in 1..CurrentNetwork::MAX_OUTPUTS * 2 {
284 let output = Output::<CurrentNetwork>::from_str(&format!("output r{i} as field.private;")).unwrap();
285
286 match function.outputs.len() < CurrentNetwork::MAX_OUTPUTS {
287 true => assert!(function.add_output(output).is_ok()),
288 false => assert!(function.add_output(output).is_err()),
289 }
290 }
291 }
292}