snarkvm_synthesizer_process/stack/
deploy.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
16use super::*;
17
18use rand::{SeedableRng, rngs::StdRng};
19
20impl<N: Network> Stack<N> {
21    /// Deploys the given program ID, if it does not exist.
22    #[inline]
23    pub fn deploy<A: circuit::Aleo<Network = N>, R: Rng + CryptoRng>(&self, rng: &mut R) -> Result<Deployment<N>> {
24        let timer = timer!("Stack::deploy");
25
26        // Ensure the program contains functions.
27        ensure!(!self.program.functions().is_empty(), "Program '{}' has no functions", self.program.id());
28
29        // Initialize a vector for the verifying keys and certificates.
30        let mut verifying_keys = Vec::with_capacity(self.program.functions().len());
31
32        for function_name in self.program.functions().keys() {
33            // Synthesize the proving and verifying key.
34            self.synthesize_key::<A, R>(function_name, rng)?;
35            lap!(timer, "Synthesize key for {function_name}");
36
37            // Retrieve the proving key.
38            let proving_key = self.get_proving_key(function_name)?;
39            // Retrieve the verifying key.
40            let verifying_key = self.get_verifying_key(function_name)?;
41            lap!(timer, "Retrieve the keys for {function_name}");
42
43            // Certify the circuit.
44            let certificate = Certificate::certify(&function_name.to_string(), &proving_key, &verifying_key)?;
45            lap!(timer, "Certify the circuit");
46
47            // Add the verifying key and certificate to the bundle.
48            verifying_keys.push((*function_name, (verifying_key, certificate)));
49        }
50
51        finish!(timer);
52
53        // Return the deployment.
54        Deployment::new(N::EDITION, self.program.clone(), verifying_keys)
55    }
56
57    /// Checks each function in the program on the given verifying key and certificate.
58    #[inline]
59    pub fn verify_deployment<A: circuit::Aleo<Network = N>, R: Rng + CryptoRng>(
60        &self,
61        deployment: &Deployment<N>,
62        rng: &mut R,
63    ) -> Result<()> {
64        let timer = timer!("Stack::verify_deployment");
65
66        // Sanity Checks //
67
68        // Ensure the deployment is ordered.
69        deployment.check_is_ordered()?;
70        // Ensure the program in the stack and deployment matches.
71        ensure!(&self.program == deployment.program(), "The stack program does not match the deployment program");
72
73        // Check Verifying Keys //
74
75        let program_id = self.program.id();
76
77        // Check that the number of combined variables does not exceed the deployment limit.
78        ensure!(deployment.num_combined_variables()? <= N::MAX_DEPLOYMENT_VARIABLES);
79        // Check that the number of combined constraints does not exceed the deployment limit.
80        ensure!(deployment.num_combined_constraints()? <= N::MAX_DEPLOYMENT_CONSTRAINTS);
81
82        // Construct the call stacks and assignments used to verify the certificates.
83        let mut call_stacks = Vec::with_capacity(deployment.verifying_keys().len());
84
85        // The `root_tvk` is `None` when verifying the deployment of an individual circuit.
86        let root_tvk = None;
87
88        // The `caller` is `None` when verifying the deployment of an individual circuit.
89        let caller = None;
90
91        // Check that the number of functions matches the number of verifying keys.
92        ensure!(
93            deployment.program().functions().len() == deployment.verifying_keys().len(),
94            "The number of functions in the program does not match the number of verifying keys"
95        );
96
97        // Create a seeded rng to use for input value and sub-stack generation.
98        // This is needed to ensure that the verification results of deployments are consistent across all parties,
99        // because currently there is a possible flakiness due to overflows in Field to Scalar casting.
100        let seed = u64::from_bytes_le(&deployment.to_deployment_id()?.to_bytes_le()?[0..8])?;
101        let mut seeded_rng = rand_chacha::ChaChaRng::seed_from_u64(seed);
102
103        // Iterate through the program functions and construct the callstacks and corresponding assignments.
104        for (function, (_, (verifying_key, _))) in
105            deployment.program().functions().values().zip_eq(deployment.verifying_keys())
106        {
107            // Initialize a burner private key.
108            let burner_private_key = PrivateKey::new(rng)?;
109            // Compute the burner address.
110            let burner_address = Address::try_from(&burner_private_key)?;
111            // Retrieve the input types.
112            let input_types = function.input_types();
113            // Sample the inputs.
114            let inputs = input_types
115                .iter()
116                .map(|input_type| match input_type {
117                    ValueType::ExternalRecord(locator) => {
118                        // Retrieve the external stack.
119                        let stack = self.get_external_stack(locator.program_id())?;
120                        // Sample the input.
121                        stack.sample_value(&burner_address, &ValueType::Record(*locator.resource()), &mut seeded_rng)
122                    }
123                    _ => self.sample_value(&burner_address, input_type, &mut seeded_rng),
124                })
125                .collect::<Result<Vec<_>>>()?;
126            lap!(timer, "Sample the inputs");
127            // Sample 'is_root'.
128            let is_root = true;
129
130            // Compute the request, with a burner private key.
131            let request = Request::sign(
132                &burner_private_key,
133                *program_id,
134                *function.name(),
135                inputs.into_iter(),
136                &input_types,
137                root_tvk,
138                is_root,
139                rng,
140            )?;
141            lap!(timer, "Compute the request for {}", function.name());
142            // Initialize the assignments.
143            let assignments = Assignments::<N>::default();
144            // Initialize the constraint limit. Account for the constraint added after synthesis that makes the Varuna zerocheck hiding.
145            let Some(constraint_limit) = verifying_key.circuit_info.num_constraints.checked_sub(1) else {
146                // Since a deployment must always pay non-zero fee, it must always have at least one constraint.
147                bail!("The constraint limit of 0 for function '{}' is invalid", function.name());
148            };
149            // Retrieve the variable limit.
150            let variable_limit = verifying_key.num_variables();
151            // Initialize the call stack.
152            let call_stack = CallStack::CheckDeployment(
153                vec![request],
154                burner_private_key,
155                assignments.clone(),
156                Some(constraint_limit as u64),
157                Some(variable_limit),
158            );
159            // Append the function name, callstack, and assignments.
160            call_stacks.push((function.name(), call_stack, assignments));
161        }
162
163        // Verify the certificates.
164        let rngs = (0..call_stacks.len()).map(|_| StdRng::from_seed(seeded_rng.gen())).collect::<Vec<_>>();
165        cfg_into_iter!(call_stacks).zip_eq(deployment.verifying_keys()).zip_eq(rngs).try_for_each(
166            |(((function_name, call_stack, assignments), (_, (verifying_key, certificate))), mut rng)| {
167                // Synthesize the circuit.
168                if let Err(err) = self.execute_function::<A, _>(call_stack, caller, root_tvk, &mut rng) {
169                    bail!("Failed to synthesize the circuit for '{function_name}': {err}")
170                }
171                // Check the certificate.
172                match assignments.read().last() {
173                    None => bail!("The assignment for function '{function_name}' is missing in '{program_id}'"),
174                    Some((assignment, _metrics)) => {
175                        // Ensure the certificate is valid.
176                        if !certificate.verify(&function_name.to_string(), assignment, verifying_key) {
177                            bail!("The certificate for function '{function_name}' is invalid in '{program_id}'")
178                        }
179                    }
180                };
181                Ok(())
182            },
183        )?;
184
185        finish!(timer);
186
187        Ok(())
188    }
189}