mod.rs
  1  // Copyright (c) 2019-2025 Alpha-Delta Network Inc.
  2  // This file is part of the deltavm 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  
 16  mod input;
 17  use input::*;
 18  
 19  mod output;
 20  use output::*;
 21  
 22  mod bytes;
 23  mod parse;
 24  
 25  use crate::{Instruction, finalize::FinalizeCore};
 26  use console::{
 27      network::prelude::*,
 28      program::{Identifier, Register, ValueType, Variant},
 29  };
 30  
 31  use indexmap::IndexSet;
 32  
 33  #[derive(Clone, PartialEq, Eq)]
 34  pub struct FunctionCore<N: Network> {
 35      /// The name of the function.
 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<N>>,
 42      /// The output statements, in order of the desired output.
 43      outputs: IndexSet<Output<N>>,
 44      /// The optional finalize logic.
 45      finalize_logic: Option<FinalizeCore<N>>,
 46  }
 47  
 48  impl<N: Network> FunctionCore<N> {
 49      /// Initializes a new function with the given name.
 50      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      /// Returns the name of the function.
 55      pub const fn name(&self) -> &Identifier<N> {
 56          &self.name
 57      }
 58  
 59      /// Returns the function inputs.
 60      pub const fn inputs(&self) -> &IndexSet<Input<N>> {
 61          &self.inputs
 62      }
 63  
 64      /// Returns the function input types.
 65      pub fn input_types(&self) -> Vec<ValueType<N>> {
 66          self.inputs.iter().map(|input| input.value_type()).cloned().collect()
 67      }
 68  
 69      /// Returns the function input type variants.
 70      pub fn input_variants(&self) -> Vec<Variant> {
 71          self.inputs.iter().map(|input| input.value_type().variant()).collect()
 72      }
 73  
 74      /// Returns the function instructions.
 75      pub fn instructions(&self) -> &[Instruction<N>] {
 76          &self.instructions
 77      }
 78  
 79      /// Returns the function outputs.
 80      pub const fn outputs(&self) -> &IndexSet<Output<N>> {
 81          &self.outputs
 82      }
 83  
 84      /// Returns the function output types.
 85      pub fn output_types(&self) -> Vec<ValueType<N>> {
 86          self.outputs.iter().map(|output| output.value_type()).cloned().collect()
 87      }
 88  
 89      /// Returns the function output type variants.
 90      pub fn output_variants(&self) -> Vec<Variant> {
 91          self.outputs.iter().map(|output| output.value_type().variant()).collect()
 92      }
 93  
 94      /// Returns the function finalize logic.
 95      pub const fn finalize_logic(&self) -> Option<&FinalizeCore<N>> {
 96          self.finalize_logic.as_ref()
 97      }
 98  
 99      /// Returns `true` if the function contains a string type.
100      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      /// Returns `true` if the function scope contains an array type with a size that exceeds the given maximum.
108      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  
116  impl<N: Network> FunctionCore<N> {
117      /// Adds the input statement to the function.
118      ///
119      /// # Errors
120      /// This method will halt if there are instructions or output statements already.
121      /// This method will halt if the maximum number of inputs has been reached.
122      /// This method will halt if the input statement was previously added.
123      /// This method will halt if a finalize logic has been added.
124      #[inline]
125      fn add_input(&mut self, input: Input<N>) -> Result<()> {
126          // Ensure there are no instructions or output statements in memory.
127          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 the maximum number of inputs has not been exceeded.
131          ensure!(self.inputs.len() < N::MAX_INPUTS, "Cannot add more than {} inputs", N::MAX_INPUTS);
132          // Ensure the input statement was not previously added.
133          ensure!(!self.inputs.contains(&input), "Cannot add duplicate input statement");
134  
135          // Ensure a finalize logic has not been added.
136          ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
137  
138          // Ensure the input register is a locator.
139          ensure!(matches!(input.register(), Register::Locator(..)), "Input register must be a locator");
140  
141          // Insert the input statement.
142          self.inputs.insert(input);
143          Ok(())
144      }
145  
146      /// Adds the given instruction to the function.
147      ///
148      /// # Errors
149      /// This method will halt if there are output statements already.
150      /// This method will halt if the maximum number of instructions has been reached.
151      /// This method will halt if a finalize logic has been added.
152      #[inline]
153      pub fn add_instruction(&mut self, instruction: Instruction<N>) -> Result<()> {
154          // Ensure that there are no output statements in memory.
155          ensure!(self.outputs.is_empty(), "Cannot add instructions after outputs have been added");
156  
157          // Ensure the maximum number of instructions has not been exceeded.
158          ensure!(
159              self.instructions.len() < N::MAX_INSTRUCTIONS,
160              "Cannot add more than {} instructions",
161              N::MAX_INSTRUCTIONS
162          );
163  
164          // Ensure a finalize logic has not been added.
165          ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
166  
167          // Ensure the destination register is a locator.
168          for register in instruction.destinations() {
169              ensure!(matches!(register, Register::Locator(..)), "Destination register must be a locator");
170          }
171  
172          // Insert the instruction.
173          self.instructions.push(instruction);
174          Ok(())
175      }
176  
177      /// Adds the output statement to the function.
178      ///
179      /// # Errors
180      /// This method will halt if the maximum number of outputs has been reached.
181      /// This method will halt if a finalize logic has been added.
182      #[inline]
183      fn add_output(&mut self, output: Output<N>) -> Result<()> {
184          // Ensure the maximum number of outputs has not been exceeded.
185          ensure!(self.outputs.len() < N::MAX_OUTPUTS, "Cannot add more than {} outputs", N::MAX_OUTPUTS);
186          // Ensure the output statement was not previously added.
187          ensure!(!self.outputs.contains(&output), "Cannot add duplicate output statement");
188  
189          // Ensure that the finalize logic has not been added.
190          ensure!(self.finalize_logic.is_none(), "Cannot add instructions after finalize logic has been added");
191  
192          // Insert the output statement.
193          self.outputs.insert(output);
194          Ok(())
195      }
196  
197      /// Adds the finalize scope to the function.
198      ///
199      /// # Errors
200      /// This method will halt if a finalize scope has already been added.
201      /// This method will halt if name in the finalize scope does not match the function name.
202      /// This method will halt if the maximum number of finalize inputs has been reached.
203      /// This method will halt if the number of finalize operands does not match the number of finalize inputs.
204      #[inline]
205      fn add_finalize(&mut self, finalize: FinalizeCore<N>) -> Result<()> {
206          // Ensure there is no finalize scope in memory.
207          ensure!(self.finalize_logic.is_none(), "Cannot add multiple finalize scopes to function '{}'", self.name);
208          // Ensure the finalize scope name matches the function name.
209          ensure!(*finalize.name() == self.name, "Finalize scope name must match function name '{}'", self.name);
210          // Ensure the number of finalize inputs has not been exceeded.
211          ensure!(finalize.inputs().len() <= N::MAX_INPUTS, "Cannot add more than {} inputs to finalize", N::MAX_INPUTS);
212  
213          // Insert the finalize scope.
214          self.finalize_logic = Some(finalize);
215          Ok(())
216      }
217  }
218  
219  impl<N: Network> TypeName for FunctionCore<N> {
220      /// Returns the type name as a string.
221      #[inline]
222      fn type_name() -> &'static str {
223          "function"
224      }
225  }
226  
227  #[cfg(test)]
228  mod 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          // Initialize a new function instance.
238          let name = Identifier::from_str("function_core_test").unwrap();
239          let mut function = Function::<CurrentNetwork>::new(name);
240  
241          // Ensure that an input can be added.
242          let input = Input::<CurrentNetwork>::from_str("input r0 as field.private;").unwrap();
243          assert!(function.add_input(input.clone()).is_ok());
244  
245          // Ensure that adding a duplicate input will fail.
246          assert!(function.add_input(input).is_err());
247  
248          // Ensure that adding more than the maximum number of inputs will fail.
249          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          // Initialize a new function instance.
262          let name = Identifier::from_str("function_core_test").unwrap();
263          let mut function = Function::<CurrentNetwork>::new(name);
264  
265          // Ensure that an instruction can be added.
266          let instruction = Instruction::<CurrentNetwork>::from_str("add r0 r1 into r2;").unwrap();
267          assert!(function.add_instruction(instruction).is_ok());
268  
269          // Ensure that adding more than the maximum number of instructions will fail.
270          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          // Initialize a new function instance.
283          let name = Identifier::from_str("function_core_test").unwrap();
284          let mut function = Function::<CurrentNetwork>::new(name);
285  
286          // Ensure that an output can be added.
287          let output = Output::<CurrentNetwork>::from_str("output r0 as field.private;").unwrap();
288          assert!(function.add_output(output).is_ok());
289  
290          // Ensure that adding more than the maximum number of outputs will fail.
291          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  }