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 && self.verifying_keys == other.verifying_keys && self.program == other.program
54 }
55}
56
57impl<N: Network> Eq for Deployment<N> {}
58
59impl<N: Network> Deployment<N> {
60 pub fn new(
62 edition: u16,
63 program: Program<N>,
64 verifying_keys: Vec<(Identifier<N>, (VerifyingKey<N>, Certificate<N>))>,
65 program_checksum: Option<[U8<N>; 32]>,
66 program_owner: Option<Address<N>>,
67 ) -> Result<Self> {
68 let deployment = Self { edition, program, verifying_keys, program_checksum, program_owner };
70 deployment.check_is_ordered()?;
72 Ok(deployment)
74 }
75
76 pub fn check_is_ordered(&self) -> Result<()> {
78 let program_id = self.program.id();
79
80 self.version()?;
83 if let Some(program_checksum) = self.program_checksum {
85 ensure!(
86 program_checksum == self.program.to_checksum(),
87 "The program checksum in the deployment does not match the computed checksum for '{program_id}'"
88 );
89 }
90 ensure!(
92 !self.program.functions().is_empty(),
93 "No functions present in the deployment for program '{program_id}'"
94 );
95 ensure!(
97 !self.verifying_keys.is_empty(),
98 "No verifying keys present in the deployment for program '{program_id}'"
99 );
100
101 if self.program.functions().len() != self.verifying_keys.len() {
103 bail!("Deployment has an incorrect number of verifying keys, according to the program.");
104 }
105
106 ensure!(
108 self.program.functions().len() <= N::MAX_FUNCTIONS,
109 "Deployment has too many functions (maximum is '{}')",
110 N::MAX_FUNCTIONS
111 );
112
113 for ((function_name, function), (name, _)) in self.program.functions().iter().zip_eq(&self.verifying_keys) {
115 if function_name != function.name() {
117 bail!("The function key is '{function_name}', but the function name is '{}'", function.name())
118 }
119 if name != function.name() {
121 bail!("The verifier key is '{name}', but the function name is '{}'", function.name())
122 }
123 }
124
125 ensure!(
126 !has_duplicates(self.verifying_keys.iter().map(|(name, ..)| name)),
127 "A duplicate function name was found"
128 );
129
130 Ok(())
131 }
132
133 pub fn size_in_bytes(&self) -> Result<u64> {
135 Ok(u64::try_from(self.to_bytes_le()?.len())?)
136 }
137
138 pub fn num_functions(&self) -> usize {
140 self.program.functions().len()
141 }
142
143 pub const fn edition(&self) -> u16 {
145 self.edition
146 }
147
148 pub const fn program(&self) -> &Program<N> {
150 &self.program
151 }
152
153 pub const fn program_checksum(&self) -> Option<[U8<N>; 32]> {
155 self.program_checksum
156 }
157
158 pub const fn program_owner(&self) -> Option<Address<N>> {
160 self.program_owner
161 }
162
163 pub const fn program_id(&self) -> &ProgramID<N> {
165 self.program.id()
166 }
167
168 pub const fn verifying_keys(&self) -> &Vec<(Identifier<N>, (VerifyingKey<N>, Certificate<N>))> {
170 &self.verifying_keys
171 }
172
173 pub fn num_combined_variables(&self) -> Result<u64> {
175 let mut num_combined_variables = 0u64;
177 for (_, (vk, _)) in &self.verifying_keys {
179 num_combined_variables = num_combined_variables
183 .checked_add(vk.num_variables())
184 .ok_or_else(|| anyhow!("Overflow when counting variables for '{}'", self.program_id()))?;
185 }
186 Ok(num_combined_variables)
188 }
189
190 pub fn num_combined_constraints(&self) -> Result<u64> {
192 let mut num_combined_constraints = 0u64;
194 for (_, (vk, _)) in &self.verifying_keys {
196 num_combined_constraints = num_combined_constraints
200 .checked_add(vk.circuit_info.num_constraints as u64)
201 .ok_or_else(|| anyhow!("Overflow when counting constraints for '{}'", self.program_id()))?;
202 }
203 Ok(num_combined_constraints)
205 }
206
207 pub fn to_deployment_id(&self) -> Result<Field<N>> {
209 Ok(*Transaction::deployment_tree(self)?.root())
210 }
211}
212
213impl<N: Network> Deployment<N> {
214 #[doc(hidden)]
217 pub fn set_program_checksum_raw(&mut self, program_checksum: Option<[U8<N>; 32]>) {
218 self.program_checksum = program_checksum;
219 }
220
221 #[doc(hidden)]
224 pub fn set_program_owner_raw(&mut self, program_owner: Option<Address<N>>) {
225 self.program_owner = program_owner;
226 }
227
228 #[doc(hidden)]
231 pub(super) fn version(&self) -> Result<DeploymentVersion> {
232 match (self.program_checksum.is_some(), self.program_owner.is_some()) {
233 (false, false) => Ok(DeploymentVersion::V1),
234 (true, true) => Ok(DeploymentVersion::V2),
235 (true, false) => {
236 bail!("The program checksum is present, but the program owner is absent.")
237 }
238 (false, true) => {
239 bail!("The program owner is present, but the program checksum is absent.")
240 }
241 }
242 }
243}
244
245#[derive(Copy, Clone, Eq, PartialEq)]
247pub(super) enum DeploymentVersion {
248 V1 = 1,
249 V2 = 2,
250}
251
252#[cfg(test)]
253pub mod test_helpers {
254 use super::*;
255 use console::network::MainnetV0;
256 use snarkvm_synthesizer_process::Process;
257
258 use std::sync::OnceLock;
259
260 type CurrentNetwork = MainnetV0;
261 type CurrentAleo = snarkvm_circuit::network::AleoV0;
262
263 pub(crate) fn sample_deployment_v1(edition: u16, rng: &mut TestRng) -> Deployment<CurrentNetwork> {
264 static INSTANCE: OnceLock<Deployment<CurrentNetwork>> = OnceLock::new();
265 let deployment = INSTANCE
266 .get_or_init(|| {
267 let (string, program) = Program::<CurrentNetwork>::parse(
269 r"
270program testing_three.aleo;
271
272mapping store:
273 key as u32.public;
274 value as u32.public;
275
276function compute:
277 input r0 as u32.private;
278 add r0 r0 into r1;
279 output r1 as u32.public;",
280 )
281 .unwrap();
282 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
283 let process = Process::load().unwrap();
285 let mut deployment = process.deploy::<CurrentAleo, _>(&program, rng).unwrap();
287 deployment.set_program_checksum_raw(None);
289 deployment.set_program_owner_raw(None);
291 Deployment::from_str(&deployment.to_string()).unwrap()
294 })
295 .clone();
296 Deployment::<CurrentNetwork>::new(
299 edition % 2,
300 deployment.program().clone(),
301 deployment.verifying_keys().clone(),
302 deployment.program_checksum(),
303 deployment.program_owner(),
304 )
305 .unwrap()
306 }
307
308 pub(crate) fn sample_deployment_v2(edition: u16, rng: &mut TestRng) -> Deployment<CurrentNetwork> {
309 static INSTANCE: OnceLock<Deployment<CurrentNetwork>> = OnceLock::new();
310 let deployment = INSTANCE
311 .get_or_init(|| {
312 let (string, program) = Program::<CurrentNetwork>::parse(
314 r"
315program testing_four.aleo;
316
317mapping store:
318 key as u32.public;
319 value as u32.public;
320
321function compute:
322 input r0 as u32.private;
323 add r0 r0 into r1;
324 output r1 as u32.public;",
325 )
326 .unwrap();
327 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
328 let process = Process::load().unwrap();
330 let mut deployment = process.deploy::<CurrentAleo, _>(&program, rng).unwrap();
332 deployment.set_program_checksum_raw(Some(deployment.program().to_checksum()));
334 deployment.set_program_owner_raw(Some(Address::rand(rng)));
336 Deployment::from_str(&deployment.to_string()).unwrap()
339 })
340 .clone();
341 Deployment::<CurrentNetwork>::new(
343 edition,
344 deployment.program().clone(),
345 deployment.verifying_keys().clone(),
346 deployment.program_checksum(),
347 deployment.program_owner(),
348 )
349 .unwrap()
350 }
351}