Skip to main content

snarkvm_synthesizer_process/stack/
deploy.rs

1// Copyright (c) 2019-2026 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
16use super::*;
17
18impl<N: Network> Stack<N> {
19    /// Deploys the given program ID, if it does not exist.
20    #[inline]
21    pub fn deploy<A: circuit::Aleo<Network = N>, R: Rng + CryptoRng>(&self, rng: &mut R) -> Result<Deployment<N>> {
22        let timer = timer!("Stack::deploy");
23
24        // Ensure the program contains functions.
25        ensure!(!self.program.functions().is_empty(), "Program '{}' has no functions", self.program.id());
26
27        // Initialize a vector for the verifying keys and certificates.
28        let mut verifying_keys = Vec::with_capacity(self.program.functions().len() + self.program.records().len());
29
30        // Synthesize function verifying keys.
31        for function_name in self.program.functions().keys() {
32            // Synthesize the proving and verifying key.
33            self.synthesize_key::<A, R>(function_name, rng)?;
34            lap!(timer, "Synthesize key for {function_name}");
35
36            // Retrieve the proving key.
37            let proving_key = self.get_proving_key(function_name)?;
38            // Retrieve the verifying key.
39            let verifying_key = self.get_verifying_key(function_name)?;
40            lap!(timer, "Retrieve the keys for {function_name}");
41
42            // Certify the circuit.
43            let certificate = Certificate::certify(&function_name.to_string(), &proving_key, &verifying_key)?;
44            lap!(timer, "Certify the circuit");
45
46            // Add the verifying key and certificate to the bundle.
47            verifying_keys.push((*function_name, (verifying_key, certificate)));
48        }
49
50        // Synthesize record (translation) verifying keys.
51        for record_name in self.program.records().keys() {
52            // Synthesize the proving and verifying key.
53            self.synthesize_translation_key::<A, R>(record_name, rng)?;
54            lap!(timer, "Synthesize key for translation circuit for {record_name}");
55
56            // Retrieve the proving key.
57            let proving_key = self.get_proving_key(record_name)?;
58
59            // Retrieve the verifying key.
60            let verifying_key = self.get_verifying_key(record_name)?;
61            lap!(timer, "Retrieve the keys for translation circuit for {record_name}");
62
63            // Certify the circuit.
64            let certificate = Certificate::certify(&record_name.to_string(), &proving_key, &verifying_key)?;
65            lap!(timer, "Certify the circuit");
66
67            // Add the verifying key and certificate to the bundle.
68            verifying_keys.push((*record_name, (verifying_key, certificate)));
69        }
70
71        finish!(timer);
72
73        // Return the deployment.
74        // Note: The owner placeholder `Address::zero()` is unconditionally overwritten by
75        // `VM::deploy()` before the deployment reaches the ledger (the caller's address is set
76        // for V9+, or the owner is cleared for pre-V9).
77        Deployment::new(
78            *self.program_edition,
79            self.program.clone(),
80            verifying_keys,
81            Some(self.program.to_checksum()),
82            Some(Address::zero()),
83        )
84    }
85
86    /// Checks each function in the program on the given verifying key and certificate.
87    #[inline]
88    pub fn verify_deployment<A: circuit::Aleo<Network = N>, R: Rng + CryptoRng>(
89        &self,
90        _consensus_version: ConsensusVersion,
91        deployment: &Deployment<N>,
92        rng: &mut R,
93    ) -> Result<()> {
94        let timer = timer!("Stack::verify_deployment");
95
96        // NOTE: As developer, you will likely still want to confirm that your
97        // deployment is within R1CS constraint and variable limits using
98        // targeted and parallelized synthesis.
99        if cfg!(all(feature = "dev_skip_checks", feature = "test_consensus_heights")) {
100            return Ok(());
101        }
102
103        // Sanity Checks //
104
105        // Ensure the deployment is ordered.
106        deployment.check_is_ordered()?;
107
108        // Ensure the program in the stack and deployment matches.
109        ensure!(&self.program == deployment.program(), "The stack program does not match the deployment program");
110        // If the deployment contains a checksum, ensure it matches the one computed by the stack.
111        if let Some(program_checksum) = deployment.program_checksum() {
112            ensure!(
113                program_checksum == self.program_checksum,
114                "The deployment checksum does not match the stack checksum"
115            );
116        }
117
118        // Check Verifying Keys //
119
120        // Get the program ID.
121        let program_id = self.program.id();
122
123        // Check that the number of combined variables does not exceed the deployment limit.
124        ensure!(deployment.num_combined_variables()? <= N::MAX_DEPLOYMENT_VARIABLES);
125        // Check that the number of combined constraints does not exceed the deployment limit.
126        ensure!(deployment.num_combined_constraints()? <= N::MAX_DEPLOYMENT_CONSTRAINTS);
127
128        // Construct the call stacks and assignments used to verify the certificates.
129        let mut call_stacks = Vec::with_capacity(deployment.function_verifying_keys().len());
130
131        // Sample a dummy `root_tvk` for circuit synthesis.
132        let root_tvk = None;
133        // Sample a dummy `caller` for circuit synthesis.
134        let caller = None;
135
136        // Check that the number of functions matches the number of function verifying keys.
137        ensure!(
138            deployment.program().functions().len() == deployment.function_verifying_keys().len(),
139            "The number of functions in the program does not match the number of function verifying keys"
140        );
141
142        #[cfg(not(any(test, feature = "test")))]
143        // Skip the certificate verification if the consensus version is before ConsensusVersion::V8.
144        // Circuit synthesis was changed in a backwards incompatible way in ConsensusVersion::V8.
145        if (ConsensusVersion::V1..=ConsensusVersion::V7).contains(&_consensus_version) {
146            finish!(timer);
147            return Ok(());
148        }
149
150        // Create a seeded rng to use for input value and sub-stack generation.
151        // This is needed to ensure that the verification results of deployments are consistent across all parties,
152        // because currently there is a possible flakiness due to overflows in Field to Scalar casting.
153        let seed = u64::from_bytes_le(&deployment.to_deployment_id()?.to_bytes_le()?[0..8])?;
154        let mut seeded_rng = rand_chacha::ChaChaRng::seed_from_u64(seed);
155
156        // Iterate through the program functions and construct the callstacks and corresponding assignments.
157        for (function, (_, (verifying_key, _))) in
158            deployment.program().functions().values().zip_eq(deployment.function_verifying_keys())
159        {
160            // Initialize a burner private key.
161            let burner_private_key = PrivateKey::new(rng)?;
162            // Compute the burner address.
163            let burner_address = Address::try_from(&burner_private_key)?;
164            // Retrieve the input types.
165            let input_types = function.input_types();
166            // Retrieve the program checksum, if the program has a constructor.
167            let program_checksum = match self.program().contains_constructor() {
168                true => Some(self.program_checksum_as_field()?),
169                false => None,
170            };
171            // Sample the inputs.
172            let inputs = input_types
173                .iter()
174                .map(|input_type| match input_type {
175                    ValueType::ExternalRecord(locator) => {
176                        // Retrieve the external stack.
177                        let stack = self.get_external_stack(locator.program_id())?;
178                        // Sample the input.
179                        stack.sample_value(
180                            &burner_address,
181                            &ValueType::Record(*locator.resource()).into(),
182                            &mut seeded_rng,
183                        )
184                    }
185                    _ => self.sample_value(&burner_address, &input_type.into(), &mut seeded_rng),
186                })
187                .collect::<Result<Vec<_>>>()?;
188            lap!(timer, "Sample the inputs");
189            // Sample a dummy 'is_root'.
190            let is_root = true;
191
192            // Compute the request, with a burner private key.
193            let request = Request::sign(
194                &burner_private_key,
195                *program_id,
196                *function.name(),
197                inputs.into_iter(),
198                &input_types,
199                root_tvk,
200                is_root,
201                program_checksum,
202                false,
203                rng,
204            )?;
205            lap!(timer, "Compute the request for {}", function.name());
206            // Initialize the assignments.
207            let assignments = Assignments::<N>::default();
208            // Initialize the constraint limit. Account for the constraint added after synthesis that makes the Varuna zerocheck hiding.
209            let Some(constraint_limit) = verifying_key.circuit_info.num_constraints.checked_sub(1) else {
210                // Since a deployment must always pay non-zero fee, it must always have at least one constraint.
211                bail!("The constraint limit of 0 for function '{}' is invalid", function.name());
212            };
213            // Retrieve the variable limit.
214            let variable_limit = verifying_key.num_variables();
215            // Initialize the call stack.
216            let call_stack = CallStack::CheckDeployment(
217                vec![request],
218                burner_private_key,
219                assignments.clone(),
220                Some(constraint_limit as u64),
221                Some(variable_limit),
222            );
223            // Append the function name, callstack, and assignments.
224            call_stacks.push((function.name(), call_stack, assignments));
225        }
226
227        // Verify the certificates.
228        let rngs = (0..call_stacks.len()).map(|_| StdRng::from_seed(seeded_rng.random())).collect::<Vec<_>>();
229        cfg_into_iter!(call_stacks).zip_eq(deployment.function_verifying_keys()).zip_eq(rngs).try_for_each(
230            |(((function_name, call_stack, assignments), (_, (verifying_key, certificate))), mut rng)| {
231                // Synthesize the circuit.
232                if let Err(err) = self.execute_function::<A, _>(call_stack, caller, root_tvk, &mut rng) {
233                    bail!("Failed to synthesize the circuit for '{function_name}': {err}")
234                }
235                // Check the certificate.
236                match assignments.read().last() {
237                    None => bail!("The assignment for function '{function_name}' is missing in '{program_id}'"),
238                    Some((assignment, _metrics)) => {
239                        // Ensure the certificate is valid.
240                        if !certificate.verify(&function_name.to_string(), assignment, verifying_key) {
241                            bail!("The certificate for function '{function_name}' is invalid in '{program_id}'")
242                        }
243                    }
244                };
245                Ok(())
246            },
247        )?;
248
249        // If the deployment contains translation verifying keys, verify them.
250        if let Some(translation_verifying_keys) = deployment.translation_verifying_keys() {
251            // Iterate through the program records and produce translation assignments.
252            ensure!(
253                deployment.program().records().len() == translation_verifying_keys.len(),
254                "The number of records in the program does not match the number of translation verifying keys"
255            );
256            let translation_names_assignments = deployment
257                .program()
258                .records()
259                .iter()
260                .map(|(record_name, _record_type)| {
261                    // Construct a random `TranslationAssignment`.
262                    let program_id = *self.program_id();
263                    let function_id = Field::<N>::from_u64(Uniform::rand(rng));
264                    let record_name = *record_name;
265                    let record_static = self.sample_record(&Address::rand(rng), &record_name, Group::rand(rng), rng)?;
266                    let record_dynamic = DynamicRecord::<N>::from_record(&record_static)?;
267                    let translation_index: u16 = Uniform::rand(rng);
268                    let tvk = Uniform::rand(rng);
269                    let record_register_index = Uniform::rand(rng);
270                    let record_view_key: Option<Field<N>> = UniformExt::rand_option(rng);
271                    let gamma: Option<Group<N>> = UniformExt::rand_option(rng);
272                    let id_dynamic = compute_console_dynamic_or_external_record_id(
273                        function_id,
274                        record_dynamic.to_fields()?,
275                        tvk,
276                        U16::new(record_register_index),
277                    )?;
278                    let is_to_static = Uniform::rand(rng);
279                    let is_external_record = Uniform::rand(rng);
280                    let id_static = Uniform::rand(rng);
281
282                    lap!(timer, "Sample the inputs to the translation circuit for record {record_name}");
283
284                    Ok((
285                        record_name,
286                        translation_index,
287                        TranslationAssignment::new(
288                            record_static,
289                            record_dynamic,
290                            program_id,
291                            function_id,
292                            record_name,
293                            is_to_static,
294                            is_external_record,
295                            tvk,
296                            record_view_key,
297                            gamma,
298                            record_register_index,
299                            id_dynamic,
300                            id_static,
301                        ),
302                    ))
303                })
304                .collect::<Result<Vec<_>>>()?;
305
306            // Verify the translation certificates.
307            // Note: `Deployment::check_is_ordered` (called above) ensures that the translation
308            // verifying keys appear in the same order as the program's record definitions, so
309            // `zip_eq` correctly pairs each record assignment with its corresponding key.
310            cfg_into_iter!(translation_names_assignments).zip_eq(translation_verifying_keys).try_for_each(
311            |((record_name, translation_index, translation_assignment), (_, (verifying_key, certificate)))| {
312                // Synthesize the circuit.
313                match translation_assignment.to_circuit_assignment::<A>(translation_index) {
314                    Err(err) => Err(anyhow!("Failed to synthesize the circuit for '{record_name}': {err}")),
315                    Ok(circuit_assignment) => {
316                        // Ensure the certificate is valid.
317                        ensure!(
318                            certificate.verify(&record_name.to_string(), &circuit_assignment, verifying_key),
319                            "The translation-circuit certificate for record '{record_name}' is invalid in '{program_id}'"
320                        );
321                        Ok(())
322                    },
323                }
324            })?;
325        }
326
327        finish!(timer);
328
329        Ok(())
330    }
331}