snarkvm_ledger_block/transaction/deployment/
mod.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkVM 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#![allow(clippy::type_complexity)]
17
18mod bytes;
19mod serialize;
20mod string;
21
22use crate::Transaction;
23use console::{
24    network::prelude::*,
25    program::{Identifier, ProgramID},
26    types::Field,
27};
28use synthesizer_program::Program;
29use synthesizer_snark::{Certificate, VerifyingKey};
30
31#[derive(Clone, PartialEq, Eq)]
32pub struct Deployment<N: Network> {
33    /// The edition.
34    edition: u16,
35    /// The program.
36    program: Program<N>,
37    /// The mapping of function names to their verifying key and certificate.
38    verifying_keys: Vec<(Identifier<N>, (VerifyingKey<N>, Certificate<N>))>,
39}
40
41impl<N: Network> Deployment<N> {
42    /// Initializes a new deployment.
43    pub fn new(
44        edition: u16,
45        program: Program<N>,
46        verifying_keys: Vec<(Identifier<N>, (VerifyingKey<N>, Certificate<N>))>,
47    ) -> Result<Self> {
48        // Construct the deployment.
49        let deployment = Self { edition, program, verifying_keys };
50        // Ensure the deployment is ordered.
51        deployment.check_is_ordered()?;
52        // Return the deployment.
53        Ok(deployment)
54    }
55
56    /// Checks that the deployment is ordered.
57    pub fn check_is_ordered(&self) -> Result<()> {
58        let program_id = self.program.id();
59
60        // Ensure the edition matches.
61        ensure!(
62            self.edition == N::EDITION,
63            "Deployed the wrong edition (expected '{}', found '{}').",
64            N::EDITION,
65            self.edition
66        );
67        // Ensure the program contains functions.
68        ensure!(
69            !self.program.functions().is_empty(),
70            "No functions present in the deployment for program '{program_id}'"
71        );
72        // Ensure the deployment contains verifying keys.
73        ensure!(
74            !self.verifying_keys.is_empty(),
75            "No verifying keys present in the deployment for program '{program_id}'"
76        );
77
78        // Ensure the number of functions matches the number of verifying keys.
79        if self.program.functions().len() != self.verifying_keys.len() {
80            bail!("Deployment has an incorrect number of verifying keys, according to the program.");
81        }
82
83        // Ensure the number of functions does not exceed the maximum.
84        ensure!(
85            self.program.functions().len() <= N::MAX_FUNCTIONS,
86            "Deployment has too many functions (maximum is '{}')",
87            N::MAX_FUNCTIONS
88        );
89
90        // Ensure the function and verifying keys correspond.
91        for ((function_name, function), (name, _)) in self.program.functions().iter().zip_eq(&self.verifying_keys) {
92            // Ensure the function name is correct.
93            if function_name != function.name() {
94                bail!("The function key is '{function_name}', but the function name is '{}'", function.name())
95            }
96            // Ensure the function name with the verifying key is correct.
97            if name != function.name() {
98                bail!("The verifier key is '{name}', but the function name is '{}'", function.name())
99            }
100        }
101
102        ensure!(
103            !has_duplicates(self.verifying_keys.iter().map(|(name, ..)| name)),
104            "A duplicate function name was found"
105        );
106
107        Ok(())
108    }
109
110    /// Returns the size in bytes.
111    pub fn size_in_bytes(&self) -> Result<u64> {
112        Ok(u64::try_from(self.to_bytes_le()?.len())?)
113    }
114
115    /// Returns the number of program functions in the deployment.
116    pub fn len(&self) -> usize {
117        self.program.functions().len()
118    }
119
120    /// Returns `true` if the deployment is empty.
121    pub fn is_empty(&self) -> bool {
122        self.program.functions().is_empty()
123    }
124
125    /// Returns the edition.
126    pub const fn edition(&self) -> u16 {
127        self.edition
128    }
129
130    /// Returns the program.
131    pub const fn program(&self) -> &Program<N> {
132        &self.program
133    }
134
135    /// Returns the program.
136    pub const fn program_id(&self) -> &ProgramID<N> {
137        self.program.id()
138    }
139
140    /// Returns the verifying keys.
141    pub const fn verifying_keys(&self) -> &Vec<(Identifier<N>, (VerifyingKey<N>, Certificate<N>))> {
142        &self.verifying_keys
143    }
144
145    /// Returns the sum of the variable counts for all functions in this deployment.
146    pub fn num_combined_variables(&self) -> Result<u64> {
147        // Initialize the accumulator.
148        let mut num_combined_variables = 0u64;
149        // Iterate over the functions.
150        for (_, (vk, _)) in &self.verifying_keys {
151            // Add the number of variables.
152            // Note: This method must be *checked* because the claimed variable count
153            // is from the user, not the synthesizer.
154            num_combined_variables = num_combined_variables
155                .checked_add(vk.num_variables())
156                .ok_or_else(|| anyhow!("Overflow when counting variables for '{}'", self.program_id()))?;
157        }
158        // Return the number of combined variables.
159        Ok(num_combined_variables)
160    }
161
162    /// Returns the sum of the constraint counts for all functions in this deployment.
163    pub fn num_combined_constraints(&self) -> Result<u64> {
164        // Initialize the accumulator.
165        let mut num_combined_constraints = 0u64;
166        // Iterate over the functions.
167        for (_, (vk, _)) in &self.verifying_keys {
168            // Add the number of constraints.
169            // Note: This method must be *checked* because the claimed constraint count
170            // is from the user, not the synthesizer.
171            num_combined_constraints = num_combined_constraints
172                .checked_add(vk.circuit_info.num_constraints as u64)
173                .ok_or_else(|| anyhow!("Overflow when counting constraints for '{}'", self.program_id()))?;
174        }
175        // Return the number of combined constraints.
176        Ok(num_combined_constraints)
177    }
178
179    /// Returns the deployment ID.
180    pub fn to_deployment_id(&self) -> Result<Field<N>> {
181        Ok(*Transaction::deployment_tree(self)?.root())
182    }
183}
184
185#[cfg(test)]
186pub mod test_helpers {
187    use super::*;
188    use console::network::MainnetV0;
189    use synthesizer_process::Process;
190
191    use once_cell::sync::OnceCell;
192
193    type CurrentNetwork = MainnetV0;
194    type CurrentAleo = circuit::network::AleoV0;
195
196    pub(crate) fn sample_deployment(rng: &mut TestRng) -> Deployment<CurrentNetwork> {
197        static INSTANCE: OnceCell<Deployment<CurrentNetwork>> = OnceCell::new();
198        INSTANCE
199            .get_or_init(|| {
200                // Initialize a new program.
201                let (string, program) = Program::<CurrentNetwork>::parse(
202                    r"
203program testing.aleo;
204
205mapping store:
206    key as u32.public;
207    value as u32.public;
208
209function compute:
210    input r0 as u32.private;
211    add r0 r0 into r1;
212    output r1 as u32.public;",
213                )
214                .unwrap();
215                assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
216
217                // Construct the process.
218                let process = Process::load().unwrap();
219                // Compute the deployment.
220                let deployment = process.deploy::<CurrentAleo, _>(&program, rng).unwrap();
221                // Return the deployment.
222                // Note: This is a testing-only hack to adhere to Rust's dependency cycle rules.
223                Deployment::from_str(&deployment.to_string()).unwrap()
224            })
225            .clone()
226    }
227}