snarkvm_ledger_block/transaction/deployment/
mod.rs1#![allow(clippy::type_complexity)]
17
18mod bytes;
19mod serialize;
20mod string;
21
22use crate::Transaction;
23use console::{
24 network::prelude::*,
25 program::{Address, Identifier, ProgramID},
26 types::{Field, U8},
27};
28use snarkvm_synthesizer_program::Program;
29use snarkvm_synthesizer_snark::{Certificate, VerifyingKey};
30
31#[derive(Clone)]
32pub struct Deployment<N: Network> {
33 edition: u16,
35 program: Program<N>,
37 verifying_keys: Vec<(Identifier<N>, (VerifyingKey<N>, Certificate<N>))>,
39 program_checksum: Option<[U8<N>; 32]>,
44 program_owner: Option<Address<N>>,
49}
50
51impl<N: Network> PartialEq for Deployment<N> {
52 fn eq(&self, other: &Self) -> bool {
53 self.edition == other.edition
54 && self.program_checksum == other.program_checksum
55 && self.program_owner == other.program_owner
56 && self.verifying_keys == other.verifying_keys
57 && self.program == other.program
58 }
59}
60
61impl<N: Network> Eq for Deployment<N> {}
62
63impl<N: Network> Deployment<N> {
64 pub fn new(
66 edition: u16,
67 program: Program<N>,
68 verifying_keys: Vec<(Identifier<N>, (VerifyingKey<N>, Certificate<N>))>,
69 program_checksum: Option<[U8<N>; 32]>,
70 program_owner: Option<Address<N>>,
71 ) -> Result<Self> {
72 let deployment = Self { edition, program, verifying_keys, program_checksum, program_owner };
74 deployment.check_is_ordered()?;
76 Ok(deployment)
78 }
79
80 pub fn check_is_ordered(&self) -> Result<()> {
82 let program_id = self.program.id();
83 let num_functions = self.program.functions().len();
84 let num_records = self.program.records().len();
85
86 self.version()?;
89
90 if let Some(program_checksum) = self.program_checksum {
92 ensure!(
93 program_checksum == self.program.to_checksum(),
94 "The program checksum in the deployment does not match the computed checksum for '{program_id}'"
95 );
96 }
97 ensure!(
99 !self.program.functions().is_empty(),
100 "No functions present in the deployment for program '{program_id}'"
101 );
102 ensure!(
104 num_functions <= N::MAX_FUNCTIONS,
105 "Deployment has too many functions (maximum is '{}')",
106 N::MAX_FUNCTIONS
107 );
108 ensure!(num_records <= N::MAX_RECORDS, "Deployment has too many records (maximum is '{}')", N::MAX_RECORDS);
110
111 ensure!(
113 !self.verifying_keys.is_empty(),
114 "No verifying keys present in the deployment for program '{program_id}'"
115 );
116 ensure!(
118 self.verifying_keys.len() == num_functions || self.verifying_keys.len() == num_functions + num_records,
119 "Deployment has an incorrect number of verifying keys, according to the program."
120 );
121 for ((function_name, function), (name, _)) in
123 self.program.functions().iter().zip_eq(&self.verifying_keys[..num_functions])
124 {
125 if function_name != function.name() {
127 bail!("The function key is '{function_name}', but the function name is '{}'", function.name())
128 }
129 if name != function.name() {
131 bail!("The verifier key is '{name}', but the function name is '{}'", function.name())
132 }
133 }
134 ensure!(
136 !has_duplicates(self.verifying_keys.iter().map(|(name, ..)| name)),
137 "A duplicate verifying key name was found"
138 );
139
140 if self.verifying_keys.len() > num_functions {
142 let record_keys = &self.verifying_keys[num_functions..];
143 ensure!(
145 num_records == record_keys.len(),
146 "Expected {} records, but {} record verifying keys were provided.",
147 num_records,
148 record_keys.len()
149 );
150 for ((record_name, record), (name, _)) in self.program.records().iter().zip_eq(record_keys) {
152 if record_name != record.name() {
154 bail!("The record key is '{record_name}', but the record name is '{}'", record.name())
155 }
156 if name != record.name() {
158 bail!("The record verifying key is '{name}', but the record name is '{}'", record.name())
159 }
160 }
161 }
162
163 Ok(())
164 }
165
166 pub fn size_in_bytes(&self) -> Result<u64> {
168 Ok(u64::try_from(self.to_bytes_le()?.len())?)
169 }
170
171 pub fn num_functions(&self) -> usize {
173 self.program.functions().len()
174 }
175
176 pub const fn edition(&self) -> u16 {
178 self.edition
179 }
180
181 pub const fn program(&self) -> &Program<N> {
183 &self.program
184 }
185
186 pub const fn program_checksum(&self) -> Option<[U8<N>; 32]> {
188 self.program_checksum
189 }
190
191 pub const fn program_owner(&self) -> Option<Address<N>> {
193 self.program_owner
194 }
195
196 pub const fn program_id(&self) -> &ProgramID<N> {
198 self.program.id()
199 }
200
201 pub const fn verifying_keys(&self) -> &Vec<(Identifier<N>, (VerifyingKey<N>, Certificate<N>))> {
204 &self.verifying_keys
205 }
206
207 pub fn function_verifying_keys(&self) -> &[(Identifier<N>, (VerifyingKey<N>, Certificate<N>))] {
209 &self.verifying_keys[..self.program.functions().len()]
210 }
211
212 pub fn translation_verifying_keys(&self) -> Option<&[(Identifier<N>, (VerifyingKey<N>, Certificate<N>))]> {
214 let num_functions = self.program.functions().len();
215 if self.verifying_keys.len() > num_functions { Some(&self.verifying_keys[num_functions..]) } else { None }
216 }
217
218 pub fn num_combined_variables(&self) -> Result<u64> {
220 self.num_combined_function_variables()?
221 .checked_add(self.num_combined_translation_variables()?)
222 .ok_or_else(|| anyhow!("Overflow when counting total variables for '{}'", self.program_id()))
223 }
224
225 pub fn num_combined_function_variables(&self) -> Result<u64> {
227 let mut num_combined_variables = 0u64;
229 for (_, (vk, _)) in self.function_verifying_keys() {
231 num_combined_variables = num_combined_variables
233 .checked_add(vk.num_variables())
234 .ok_or_else(|| anyhow!("Overflow when counting variables for '{}'", self.program_id()))?;
235 }
236 Ok(num_combined_variables)
238 }
239
240 pub fn num_combined_translation_variables(&self) -> Result<u64> {
242 let mut num_combined_variables = 0u64;
244 if let Some(record_vks) = self.translation_verifying_keys() {
246 for (_, (vk, _)) in record_vks {
247 num_combined_variables = num_combined_variables
249 .checked_add(vk.num_variables())
250 .ok_or_else(|| anyhow!("Overflow when counting variables for '{}'", self.program_id()))?;
251 }
252 }
253 Ok(num_combined_variables)
255 }
256
257 pub fn num_combined_constraints(&self) -> Result<u64> {
259 self.num_combined_function_constraints()?
260 .checked_add(self.num_combined_translation_constraints()?)
261 .ok_or_else(|| anyhow!("Overflow when counting total constraints for '{}'", self.program_id()))
262 }
263
264 pub fn num_combined_function_constraints(&self) -> Result<u64> {
266 let mut num_combined_constraints = 0u64;
268 for (_, (vk, _)) in self.function_verifying_keys() {
270 num_combined_constraints = num_combined_constraints
272 .checked_add(vk.circuit_info.num_constraints as u64)
273 .ok_or_else(|| anyhow!("Overflow when counting constraints for '{}'", self.program_id()))?;
274 }
275 Ok(num_combined_constraints)
277 }
278
279 pub fn num_combined_translation_constraints(&self) -> Result<u64> {
281 let mut num_combined_constraints = 0u64;
283 if let Some(record_vks) = self.translation_verifying_keys() {
285 for (_, (vk, _)) in record_vks {
286 num_combined_constraints = num_combined_constraints
288 .checked_add(vk.circuit_info.num_constraints as u64)
289 .ok_or_else(|| anyhow!("Overflow when counting constraints for '{}'", self.program_id()))?;
290 }
291 }
292 Ok(num_combined_constraints)
294 }
295
296 pub fn to_deployment_id(&self) -> Result<Field<N>> {
298 Ok(*Transaction::deployment_tree(self)?.root())
299 }
300}
301
302impl<N: Network> Deployment<N> {
303 pub fn set_edition_raw(&mut self, edition: u16) {
305 self.edition = edition;
306 }
307
308 pub fn set_program_checksum_raw(&mut self, program_checksum: Option<[U8<N>; 32]>) {
310 self.program_checksum = program_checksum;
311 }
312
313 pub fn set_program_owner_raw(&mut self, program_owner: Option<Address<N>>) {
315 self.program_owner = program_owner;
316 }
317
318 #[doc(hidden)]
321 pub fn remove_verifying_key(&mut self, name: &Identifier<N>) {
322 self.verifying_keys.retain(|(n, _)| n != name);
323 }
324
325 #[doc(hidden)]
328 pub fn remove_verifying_keys(&mut self, names: &[Identifier<N>]) {
329 self.verifying_keys.retain(|(n, _)| !names.contains(n));
330 }
331
332 pub fn version(&self) -> Result<DeploymentVersion> {
335 match (self.program_checksum.is_some(), self.program_owner.is_some()) {
336 (false, false) => Ok(DeploymentVersion::V1),
338 (true, true) => Ok(DeploymentVersion::V2),
340 (true, false) => Ok(DeploymentVersion::V3),
342 (false, true) => {
344 bail!("The program owner is present, but the program checksum is absent.")
345 }
346 }
347 }
348}
349
350#[derive(Copy, Clone, Eq, PartialEq)]
352pub enum DeploymentVersion {
353 V1 = 1,
356 V2 = 2,
359 V3 = 3,
362}
363
364#[cfg(test)]
365pub mod test_helpers {
366 use super::*;
367 use console::network::MainnetV0;
368 use snarkvm_synthesizer_process::Process;
369
370 use std::sync::OnceLock;
371
372 type CurrentNetwork = MainnetV0;
373 type CurrentAleo = snarkvm_circuit::network::AleoV0;
374
375 pub(crate) fn sample_deployment_v1(edition: u16, rng: &mut TestRng) -> Deployment<CurrentNetwork> {
376 static INSTANCE: OnceLock<Deployment<CurrentNetwork>> = OnceLock::new();
377 let deployment = INSTANCE
378 .get_or_init(|| {
379 let (string, program) = Program::<CurrentNetwork>::parse(
381 r"
382program testing_three.aleo;
383
384mapping store:
385 key as u32.public;
386 value as u32.public;
387
388function compute:
389 input r0 as u32.private;
390 add r0 r0 into r1;
391 output r1 as u32.public;",
392 )
393 .unwrap();
394 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
395 let process = Process::load().unwrap();
397 let deployment = process.deploy::<CurrentAleo, _>(&program, rng).unwrap();
399 Deployment::from_str(&deployment.to_string()).unwrap()
402 })
403 .clone();
404 Deployment::<CurrentNetwork>::new(
407 edition % 2,
408 deployment.program().clone(),
409 deployment.verifying_keys().clone(),
410 None,
411 None,
412 )
413 .unwrap()
414 }
415
416 pub(crate) fn sample_deployment_v2_without_translation_keys(
417 edition: u16,
418 rng: &mut TestRng,
419 ) -> Deployment<CurrentNetwork> {
420 static INSTANCE: OnceLock<Deployment<CurrentNetwork>> = OnceLock::new();
421 let deployment = INSTANCE
422 .get_or_init(|| {
423 let (string, program) = Program::<CurrentNetwork>::parse(
425 r"
426program testing_four.aleo;
427
428mapping store:
429 key as u32.public;
430 value as u32.public;
431
432function compute:
433 input r0 as u32.private;
434 add r0 r0 into r1;
435 output r1 as u32.public;",
436 )
437 .unwrap();
438 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
439 let process = Process::load().unwrap();
441 let deployment = process.deploy::<CurrentAleo, _>(&program, rng).unwrap();
443 Deployment::from_str(&deployment.to_string()).unwrap()
446 })
447 .clone();
448 Deployment::<CurrentNetwork>::new(
450 edition,
451 deployment.program().clone(),
452 deployment.verifying_keys().clone(),
453 deployment.program_checksum(),
454 Some(Address::rand(rng)),
455 )
456 .unwrap()
457 }
458
459 pub(crate) fn sample_deployment_v2_with_translation_keys(
460 edition: u16,
461 rng: &mut TestRng,
462 ) -> Deployment<CurrentNetwork> {
463 static INSTANCE: OnceLock<Deployment<CurrentNetwork>> = OnceLock::new();
464 let deployment = INSTANCE
465 .get_or_init(|| {
466 let (string, program) = Program::<CurrentNetwork>::parse(
468 r"
469program testing_five.aleo;
470
471record data:
472 owner as address.private;
473 one as field.private;
474 two as group.public;
475
476mapping store:
477 key as u32.public;
478 value as u32.public;
479
480function compute:
481 input r0 as u32.private;
482 add r0 r0 into r1;
483 output r1 as u32.public;",
484 )
485 .unwrap();
486 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
487 let process = Process::load().unwrap();
489 let deployment = process.deploy::<CurrentAleo, _>(&program, rng).unwrap();
491 Deployment::from_str(&deployment.to_string()).unwrap()
494 })
495 .clone();
496 Deployment::<CurrentNetwork>::new(
498 edition,
499 deployment.program().clone(),
500 deployment.verifying_keys().clone(),
501 deployment.program_checksum(),
502 Some(Address::rand(rng)),
503 )
504 .unwrap()
505 }
506
507 pub(crate) fn sample_deployment_v3(edition: u16, rng: &mut TestRng) -> Deployment<CurrentNetwork> {
510 let mut deployment = sample_deployment_v2_with_translation_keys(edition, rng);
512 deployment.set_program_owner_raw(None);
513 deployment
514 }
515}