snarkvm_synthesizer_program/closure/
mod.rs

1// Copyright 2024 Aleo Network Foundation
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16mod input;
17use input::*;
18
19mod output;
20use output::*;
21
22mod bytes;
23mod parse;
24
25use crate::InstructionTrait;
26use console::{
27    network::prelude::*,
28    program::{Identifier, Register, RegisterType},
29};
30
31use indexmap::IndexSet;
32
33#[derive(Clone, PartialEq, Eq)]
34pub struct ClosureCore<N: Network, Instruction: InstructionTrait<N>> {
35    /// The name of the closure.
36    name: Identifier<N>,
37    /// The input statements, added in order of the input registers.
38    /// Input assignments are ensured to match the ordering of the input statements.
39    inputs: IndexSet<Input<N>>,
40    /// The instructions, in order of execution.
41    instructions: Vec<Instruction>,
42    /// The output statements, in order of the desired output.
43    outputs: IndexSet<Output<N>>,
44}
45
46impl<N: Network, Instruction: InstructionTrait<N>> ClosureCore<N, Instruction> {
47    /// Initializes a new closure with the given name.
48    pub fn new(name: Identifier<N>) -> Self {
49        Self { name, inputs: IndexSet::new(), instructions: Vec::new(), outputs: IndexSet::new() }
50    }
51
52    /// Returns the name of the closure.
53    pub const fn name(&self) -> &Identifier<N> {
54        &self.name
55    }
56
57    /// Returns the closure inputs.
58    pub const fn inputs(&self) -> &IndexSet<Input<N>> {
59        &self.inputs
60    }
61
62    /// Returns the closure instructions.
63    pub fn instructions(&self) -> &[Instruction] {
64        &self.instructions
65    }
66
67    /// Returns the closure outputs.
68    pub const fn outputs(&self) -> &IndexSet<Output<N>> {
69        &self.outputs
70    }
71}
72
73impl<N: Network, Instruction: InstructionTrait<N>> ClosureCore<N, Instruction> {
74    /// Adds the input statement to the closure.
75    ///
76    /// # Errors
77    /// This method will halt if there are instructions or output statements already.
78    /// This method will halt if the maximum number of inputs has been reached.
79    /// This method will halt if the input statement was previously added.
80    #[inline]
81    fn add_input(&mut self, input: Input<N>) -> Result<()> {
82        // Ensure there are no instructions or output statements in memory.
83        ensure!(self.instructions.is_empty(), "Cannot add inputs after instructions have been added");
84        ensure!(self.outputs.is_empty(), "Cannot add inputs after outputs have been added");
85
86        // Ensure the maximum number of inputs has not been exceeded.
87        ensure!(self.inputs.len() < N::MAX_INPUTS, "Cannot add more than {} inputs", N::MAX_INPUTS);
88        // Ensure the input statement was not previously added.
89        ensure!(!self.inputs.contains(&input), "Cannot add duplicate input statement");
90
91        // Ensure the input register is a locator.
92        ensure!(matches!(input.register(), Register::Locator(..)), "Input register must be a locator");
93
94        // Insert the input statement.
95        self.inputs.insert(input);
96        Ok(())
97    }
98
99    /// Adds the given instruction to the closure.
100    ///
101    /// # Errors
102    /// This method will halt if there are output statements already.
103    /// This method will halt if the maximum number of instructions has been reached.
104    #[inline]
105    pub fn add_instruction(&mut self, instruction: Instruction) -> Result<()> {
106        // Ensure that there are no outputs in memory.
107        ensure!(self.outputs.is_empty(), "Cannot add instructions after outputs have been added");
108
109        // Ensure the maximum number of instructions has not been exceeded.
110        ensure!(
111            self.instructions.len() < N::MAX_INSTRUCTIONS,
112            "Cannot add more than {} instructions",
113            N::MAX_INSTRUCTIONS
114        );
115
116        // Ensure the destination register is a locator.
117        for register in instruction.destinations() {
118            ensure!(matches!(register, Register::Locator(..)), "Destination register must be a locator");
119        }
120
121        // Insert the instruction.
122        self.instructions.push(instruction);
123        Ok(())
124    }
125
126    /// Adds the output statement to the closure.
127    ///
128    /// # Errors
129    /// This method will halt if the maximum number of outputs has been reached.
130    #[inline]
131    fn add_output(&mut self, output: Output<N>) -> Result<()> {
132        // Ensure the maximum number of outputs has not been exceeded.
133        ensure!(self.outputs.len() < N::MAX_OUTPUTS, "Cannot add more than {} outputs", N::MAX_OUTPUTS);
134
135        // Ensure the closure output register is not a record.
136        ensure!(!matches!(output.register_type(), RegisterType::Record(..)), "Output register cannot be a record");
137
138        // Insert the output statement.
139        self.outputs.insert(output);
140        Ok(())
141    }
142}
143
144impl<N: Network, Instruction: InstructionTrait<N>> TypeName for ClosureCore<N, Instruction> {
145    /// Returns the type name as a string.
146    #[inline]
147    fn type_name() -> &'static str {
148        "closure"
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155
156    use crate::{Closure, Instruction};
157
158    type CurrentNetwork = console::network::MainnetV0;
159
160    #[test]
161    fn test_add_input() {
162        // Initialize a new closure instance.
163        let name = Identifier::from_str("closure_core_test").unwrap();
164        let mut closure = Closure::<CurrentNetwork>::new(name);
165
166        // Ensure that an input can be added.
167        let input = Input::<CurrentNetwork>::from_str("input r0 as field;").unwrap();
168        assert!(closure.add_input(input.clone()).is_ok());
169
170        // Ensure that adding a duplicate input will fail.
171        assert!(closure.add_input(input).is_err());
172
173        // Ensure that adding more than the maximum number of inputs will fail.
174        for i in 1..CurrentNetwork::MAX_INPUTS * 2 {
175            let input = Input::<CurrentNetwork>::from_str(&format!("input r{i} as field;")).unwrap();
176
177            match closure.inputs.len() < CurrentNetwork::MAX_INPUTS {
178                true => assert!(closure.add_input(input).is_ok()),
179                false => assert!(closure.add_input(input).is_err()),
180            }
181        }
182    }
183
184    #[test]
185    fn test_add_instruction() {
186        // Initialize a new closure instance.
187        let name = Identifier::from_str("closure_core_test").unwrap();
188        let mut closure = Closure::<CurrentNetwork>::new(name);
189
190        // Ensure that an instruction can be added.
191        let instruction = Instruction::<CurrentNetwork>::from_str("add r0 r1 into r2;").unwrap();
192        assert!(closure.add_instruction(instruction).is_ok());
193
194        // Ensure that adding more than the maximum number of instructions will fail.
195        for i in 3..CurrentNetwork::MAX_INSTRUCTIONS * 2 {
196            let instruction = Instruction::<CurrentNetwork>::from_str(&format!("add r0 r1 into r{i};")).unwrap();
197
198            match closure.instructions.len() < CurrentNetwork::MAX_INSTRUCTIONS {
199                true => assert!(closure.add_instruction(instruction).is_ok()),
200                false => assert!(closure.add_instruction(instruction).is_err()),
201            }
202        }
203    }
204
205    #[test]
206    fn test_add_output() {
207        // Initialize a new closure instance.
208        let name = Identifier::from_str("closure_core_test").unwrap();
209        let mut closure = Closure::<CurrentNetwork>::new(name);
210
211        // Ensure that an output can be added.
212        let output = Output::<CurrentNetwork>::from_str("output r0 as field;").unwrap();
213        assert!(closure.add_output(output).is_ok());
214
215        // Ensure that adding more than the maximum number of outputs will fail.
216        for i in 1..CurrentNetwork::MAX_OUTPUTS * 2 {
217            let output = Output::<CurrentNetwork>::from_str(&format!("output r{i} as field;")).unwrap();
218
219            match closure.outputs.len() < CurrentNetwork::MAX_OUTPUTS {
220                true => assert!(closure.add_output(output).is_ok()),
221                false => assert!(closure.add_output(output).is_err()),
222            }
223        }
224    }
225}