snarkvm_synthesizer_program/finalize/
mod.rs1use crate::Command;
17
18mod input;
19use input::*;
20
21mod bytes;
22mod parse;
23
24use console::{
25 network::prelude::*,
26 program::{FinalizeType, Identifier, Register},
27};
28
29use indexmap::IndexSet;
30use std::collections::HashMap;
31
32#[derive(Clone, PartialEq, Eq)]
33pub struct FinalizeCore<N: Network> {
34 name: Identifier<N>,
36 inputs: IndexSet<Input<N>>,
39 commands: Vec<Command<N>>,
41 num_writes: u16,
43 positions: HashMap<Identifier<N>, usize>,
45}
46
47impl<N: Network> FinalizeCore<N> {
48 pub fn new(name: Identifier<N>) -> Self {
50 Self { name, inputs: IndexSet::new(), commands: Vec::new(), num_writes: 0, positions: HashMap::new() }
51 }
52
53 pub const fn name(&self) -> &Identifier<N> {
55 &self.name
56 }
57
58 pub const fn inputs(&self) -> &IndexSet<Input<N>> {
60 &self.inputs
61 }
62
63 pub fn input_types(&self) -> Vec<FinalizeType<N>> {
65 self.inputs.iter().map(|input| input.finalize_type()).cloned().collect()
66 }
67
68 pub fn commands(&self) -> &[Command<N>] {
70 &self.commands
71 }
72
73 pub const fn num_writes(&self) -> u16 {
75 self.num_writes
76 }
77
78 pub const fn positions(&self) -> &HashMap<Identifier<N>, usize> {
80 &self.positions
81 }
82}
83
84impl<N: Network> FinalizeCore<N> {
85 #[inline]
92 fn add_input(&mut self, input: Input<N>) -> Result<()> {
93 ensure!(self.commands.is_empty(), "Cannot add inputs after commands have been added");
95
96 ensure!(self.inputs.len() < N::MAX_INPUTS, "Cannot add more than {} inputs", N::MAX_INPUTS);
98 ensure!(!self.inputs.contains(&input), "Cannot add duplicate input statement");
100
101 ensure!(matches!(input.register(), Register::Locator(..)), "Input register must be a locator");
103
104 self.inputs.insert(input);
106 Ok(())
107 }
108
109 #[inline]
114 pub fn add_command(&mut self, command: Command<N>) -> Result<()> {
115 ensure!(self.commands.len() < N::MAX_COMMANDS, "Cannot add more than {} commands", N::MAX_COMMANDS);
117 if command.is_write() {
119 ensure!(
120 self.num_writes < N::MAX_WRITES,
121 "Cannot add more than {} 'set' & 'remove' commands",
122 N::MAX_WRITES
123 );
124 }
125
126 ensure!(!command.is_async(), "Forbidden operation: Finalize cannot invoke an 'async' instruction");
128 ensure!(!command.is_call(), "Forbidden operation: Finalize cannot invoke a 'call'");
130 ensure!(!command.is_cast_to_record(), "Forbidden operation: Finalize cannot cast to a record");
132
133 for register in command.destinations() {
135 ensure!(matches!(register, Register::Locator(..)), "Destination register must be a locator");
137 }
138
139 if let Some(position) = command.branch_to() {
141 ensure!(!self.positions.contains_key(position), "Cannot branch to an earlier position '{position}'");
143 }
144
145 if let Some(position) = command.position() {
147 ensure!(!self.positions.contains_key(position), "Cannot redefine position '{position}'");
149 ensure!(self.positions.len() < N::MAX_POSITIONS, "Cannot add more than {} positions", N::MAX_POSITIONS);
151 self.positions.insert(*position, self.commands.len());
153 }
154
155 if command.is_write() {
157 self.num_writes += 1;
159 }
160
161 self.commands.push(command);
163 Ok(())
164 }
165}
166
167impl<N: Network> TypeName for FinalizeCore<N> {
168 #[inline]
170 fn type_name() -> &'static str {
171 "finalize"
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178
179 use crate::{Command, Finalize};
180
181 type CurrentNetwork = console::network::MainnetV0;
182
183 #[test]
184 fn test_add_input() {
185 let name = Identifier::from_str("finalize_core_test").unwrap();
187 let mut finalize = Finalize::<CurrentNetwork>::new(name);
188
189 let input = Input::<CurrentNetwork>::from_str("input r0 as field.public;").unwrap();
191 assert!(finalize.add_input(input.clone()).is_ok());
192
193 assert!(finalize.add_input(input).is_err());
195
196 for i in 1..CurrentNetwork::MAX_INPUTS * 2 {
198 let input = Input::<CurrentNetwork>::from_str(&format!("input r{i} as field.public;")).unwrap();
199
200 match finalize.inputs.len() < CurrentNetwork::MAX_INPUTS {
201 true => assert!(finalize.add_input(input).is_ok()),
202 false => assert!(finalize.add_input(input).is_err()),
203 }
204 }
205 }
206
207 #[test]
208 fn test_add_command() {
209 let name = Identifier::from_str("finalize_core_test").unwrap();
211 let mut finalize = Finalize::<CurrentNetwork>::new(name);
212
213 let command = Command::<CurrentNetwork>::from_str("add r0 r1 into r2;").unwrap();
215 assert!(finalize.add_command(command).is_ok());
216
217 for i in 3..CurrentNetwork::MAX_COMMANDS * 2 {
219 let command = Command::<CurrentNetwork>::from_str(&format!("add r0 r1 into r{i};")).unwrap();
220
221 match finalize.commands.len() < CurrentNetwork::MAX_COMMANDS {
222 true => assert!(finalize.add_command(command).is_ok()),
223 false => assert!(finalize.add_command(command).is_err()),
224 }
225 }
226
227 let name = Identifier::from_str("finalize_core_test").unwrap();
231 let mut finalize = Finalize::<CurrentNetwork>::new(name);
232
233 for _ in 0..CurrentNetwork::MAX_WRITES * 2 {
234 let command = Command::<CurrentNetwork>::from_str("remove object[r0];").unwrap();
235
236 match finalize.commands.len() < CurrentNetwork::MAX_WRITES as usize {
237 true => assert!(finalize.add_command(command).is_ok()),
238 false => assert!(finalize.add_command(command).is_err()),
239 }
240 }
241 }
242
243 #[test]
244 fn test_add_command_duplicate_positions() {
245 let name = Identifier::from_str("finalize_core_test").unwrap();
247 let mut finalize = Finalize::<CurrentNetwork>::new(name);
248
249 let command = Command::<CurrentNetwork>::from_str("position start;").unwrap();
251 assert!(finalize.add_command(command.clone()).is_ok());
252
253 assert!(finalize.add_command(command).is_err());
255
256 #[allow(clippy::cast_possible_truncation)]
258 fn to_unique_string(mut n: usize) -> String {
259 let mut s = String::new();
260 while n > 0 {
261 s.push((b'A' + (n % 26) as u8) as char);
262 n /= 26;
263 }
264 s.chars().rev().collect::<String>()
265 }
266
267 for i in 1..u8::MAX as usize * 2 {
269 let position = to_unique_string(i);
270 let command = Command::<CurrentNetwork>::from_str(&format!("position {position};")).unwrap();
272
273 match finalize.commands.len() < u8::MAX as usize {
274 true => assert!(finalize.add_command(command).is_ok()),
275 false => assert!(finalize.add_command(command).is_err()),
276 }
277 }
278 }
279}