snarkvm_synthesizer_program/finalize/
mod.rs1mod input;
17use input::*;
18
19mod bytes;
20mod parse;
21
22use crate::traits::CommandTrait;
23use console::{
24 network::prelude::*,
25 program::{FinalizeType, Identifier, Register},
26};
27
28use indexmap::IndexSet;
29use std::collections::HashMap;
30
31#[derive(Clone, PartialEq, Eq)]
32pub struct FinalizeCore<N: Network, Command: CommandTrait<N>> {
33 name: Identifier<N>,
35 inputs: IndexSet<Input<N>>,
38 commands: Vec<Command>,
40 num_writes: u16,
42 positions: HashMap<Identifier<N>, usize>,
44}
45
46impl<N: Network, Command: CommandTrait<N>> FinalizeCore<N, Command> {
47 pub fn new(name: Identifier<N>) -> Self {
49 Self { name, inputs: IndexSet::new(), commands: Vec::new(), num_writes: 0, positions: HashMap::new() }
50 }
51
52 pub const fn name(&self) -> &Identifier<N> {
54 &self.name
55 }
56
57 pub const fn inputs(&self) -> &IndexSet<Input<N>> {
59 &self.inputs
60 }
61
62 pub fn input_types(&self) -> Vec<FinalizeType<N>> {
64 self.inputs.iter().map(|input| input.finalize_type()).cloned().collect()
65 }
66
67 pub fn commands(&self) -> &[Command] {
69 &self.commands
70 }
71
72 pub const fn num_writes(&self) -> u16 {
74 self.num_writes
75 }
76
77 pub const fn positions(&self) -> &HashMap<Identifier<N>, usize> {
79 &self.positions
80 }
81}
82
83impl<N: Network, Command: CommandTrait<N>> FinalizeCore<N, Command> {
84 #[inline]
91 fn add_input(&mut self, input: Input<N>) -> Result<()> {
92 ensure!(self.commands.is_empty(), "Cannot add inputs after commands have been added");
94
95 ensure!(self.inputs.len() < N::MAX_INPUTS, "Cannot add more than {} inputs", N::MAX_INPUTS);
97 ensure!(!self.inputs.contains(&input), "Cannot add duplicate input statement");
99
100 ensure!(matches!(input.register(), Register::Locator(..)), "Input register must be a locator");
102
103 self.inputs.insert(input);
105 Ok(())
106 }
107
108 #[inline]
113 pub fn add_command(&mut self, command: Command) -> Result<()> {
114 ensure!(self.commands.len() < N::MAX_COMMANDS, "Cannot add more than {} commands", N::MAX_COMMANDS);
116 ensure!(self.num_writes < N::MAX_WRITES, "Cannot add more than {} 'set' & 'remove' commands", N::MAX_WRITES);
118
119 ensure!(!command.is_call(), "Forbidden operation: Finalize cannot invoke a 'call'");
121 ensure!(!command.is_cast_to_record(), "Forbidden operation: Finalize cannot cast to a record");
123
124 for register in command.destinations() {
126 ensure!(matches!(register, Register::Locator(..)), "Destination register must be a locator");
128 }
129
130 if let Some(position) = command.branch_to() {
132 ensure!(!self.positions.contains_key(position), "Cannot branch to an earlier position '{position}'");
134 }
135
136 if let Some(position) = command.position() {
138 ensure!(!self.positions.contains_key(position), "Cannot redefine position '{position}'");
140 ensure!(self.positions.len() < u8::MAX as usize, "Cannot add more than {} positions", u8::MAX);
142 self.positions.insert(*position, self.commands.len());
144 }
145
146 if command.is_write() {
148 self.num_writes += 1;
150 }
151
152 self.commands.push(command);
154 Ok(())
155 }
156}
157
158impl<N: Network, Command: CommandTrait<N>> TypeName for FinalizeCore<N, Command> {
159 #[inline]
161 fn type_name() -> &'static str {
162 "finalize"
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 use crate::{Command, Finalize};
171
172 type CurrentNetwork = console::network::MainnetV0;
173
174 #[test]
175 fn test_add_input() {
176 let name = Identifier::from_str("finalize_core_test").unwrap();
178 let mut finalize = Finalize::<CurrentNetwork>::new(name);
179
180 let input = Input::<CurrentNetwork>::from_str("input r0 as field.public;").unwrap();
182 assert!(finalize.add_input(input.clone()).is_ok());
183
184 assert!(finalize.add_input(input).is_err());
186
187 for i in 1..CurrentNetwork::MAX_INPUTS * 2 {
189 let input = Input::<CurrentNetwork>::from_str(&format!("input r{i} as field.public;")).unwrap();
190
191 match finalize.inputs.len() < CurrentNetwork::MAX_INPUTS {
192 true => assert!(finalize.add_input(input).is_ok()),
193 false => assert!(finalize.add_input(input).is_err()),
194 }
195 }
196 }
197
198 #[test]
199 fn test_add_command() {
200 let name = Identifier::from_str("finalize_core_test").unwrap();
202 let mut finalize = Finalize::<CurrentNetwork>::new(name);
203
204 let command = Command::<CurrentNetwork>::from_str("add r0 r1 into r2;").unwrap();
206 assert!(finalize.add_command(command).is_ok());
207
208 for i in 3..CurrentNetwork::MAX_COMMANDS * 2 {
210 let command = Command::<CurrentNetwork>::from_str(&format!("add r0 r1 into r{i};")).unwrap();
211
212 match finalize.commands.len() < CurrentNetwork::MAX_COMMANDS {
213 true => assert!(finalize.add_command(command).is_ok()),
214 false => assert!(finalize.add_command(command).is_err()),
215 }
216 }
217
218 let name = Identifier::from_str("finalize_core_test").unwrap();
222 let mut finalize = Finalize::<CurrentNetwork>::new(name);
223
224 for _ in 0..CurrentNetwork::MAX_WRITES * 2 {
225 let command = Command::<CurrentNetwork>::from_str("remove object[r0];").unwrap();
226
227 match finalize.commands.len() < CurrentNetwork::MAX_WRITES as usize {
228 true => assert!(finalize.add_command(command).is_ok()),
229 false => assert!(finalize.add_command(command).is_err()),
230 }
231 }
232 }
233
234 #[test]
235 fn test_add_command_duplicate_positions() {
236 let name = Identifier::from_str("finalize_core_test").unwrap();
238 let mut finalize = Finalize::<CurrentNetwork>::new(name);
239
240 let command = Command::<CurrentNetwork>::from_str("position start;").unwrap();
242 assert!(finalize.add_command(command.clone()).is_ok());
243
244 assert!(finalize.add_command(command).is_err());
246
247 #[allow(clippy::cast_possible_truncation)]
249 fn to_unique_string(mut n: usize) -> String {
250 let mut s = String::new();
251 while n > 0 {
252 s.push((b'A' + (n % 26) as u8) as char);
253 n /= 26;
254 }
255 s.chars().rev().collect::<String>()
256 }
257
258 for i in 1..u8::MAX as usize * 2 {
260 let position = to_unique_string(i);
261 println!("position: {}", position);
262 let command = Command::<CurrentNetwork>::from_str(&format!("position {position};")).unwrap();
263
264 match finalize.commands.len() < u8::MAX as usize {
265 true => assert!(finalize.add_command(command).is_ok()),
266 false => assert!(finalize.add_command(command).is_err()),
267 }
268 }
269 }
270}