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 }