deploy.rs
1 // Copyright (c) 2025 ADnet Contributors 2 // This file is part of the AlphaVM 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 use super::*; 17 18 use rand::{SeedableRng, rngs::StdRng}; 19 20 impl<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(*self.program_edition, self.program.clone(), verifying_keys, None, None) 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 _consensus_version: ConsensusVersion, 62 deployment: &Deployment<N>, 63 rng: &mut R, 64 ) -> Result<()> { 65 let timer = timer!("Stack::verify_deployment"); 66 67 // NOTE: As developer, you will likely still want to confirm that your 68 // deployment is within R1CS constraint and variable limits using 69 // targeted and parallelized synthesis. 70 if cfg!(all(feature = "dev_skip_checks", feature = "test_consensus_heights")) { 71 return Ok(()); 72 } 73 74 // Sanity Checks // 75 76 // Ensure the deployment is ordered. 77 deployment.check_is_ordered()?; 78 79 // Ensure the program in the stack and deployment matches. 80 ensure!(&self.program == deployment.program(), "The stack program does not match the deployment program"); 81 // If the deployment contains a checksum, ensure it matches the one computed by the stack. 82 if let Some(program_checksum) = deployment.program_checksum() { 83 ensure!( 84 program_checksum == self.program_checksum, 85 "The deployment checksum does not match the stack checksum" 86 ); 87 } 88 89 // Check Verifying Keys // 90 91 // Get the program ID. 92 let program_id = self.program.id(); 93 94 // Check that the number of combined variables does not exceed the deployment limit. 95 ensure!(deployment.num_combined_variables()? <= N::MAX_DEPLOYMENT_VARIABLES); 96 // Check that the number of combined constraints does not exceed the deployment limit. 97 ensure!(deployment.num_combined_constraints()? <= N::MAX_DEPLOYMENT_CONSTRAINTS); 98 99 // Construct the call stacks and assignments used to verify the certificates. 100 let mut call_stacks = Vec::with_capacity(deployment.verifying_keys().len()); 101 102 // Sample a dummy `root_tvk` for circuit synthesis. 103 let root_tvk = None; 104 // Sample a dummy `caller` for circuit synthesis. 105 let caller = None; 106 107 // Check that the number of functions matches the number of verifying keys. 108 ensure!( 109 deployment.program().functions().len() == deployment.verifying_keys().len(), 110 "The number of functions in the program does not match the number of verifying keys" 111 ); 112 113 #[cfg(not(any(test, feature = "test")))] 114 // Skip the certificate verification if the consensus version is before ConsensusVersion::V8. 115 // Circuit synthesis was changed in a backwards incompatible way in ConsensusVersion::V8. 116 if (ConsensusVersion::V1..=ConsensusVersion::V7).contains(&_consensus_version) { 117 finish!(timer); 118 return Ok(()); 119 } 120 121 // Create a seeded rng to use for input value and sub-stack generation. 122 // This is needed to ensure that the verification results of deployments are consistent across all parties, 123 // because currently there is a possible flakiness due to overflows in Field to Scalar casting. 124 let seed = u64::from_bytes_le(&deployment.to_deployment_id()?.to_bytes_le()?[0..8])?; 125 let mut seeded_rng = rand_chacha::ChaChaRng::seed_from_u64(seed); 126 127 // Iterate through the program functions and construct the callstacks and corresponding assignments. 128 for (function, (_, (verifying_key, _))) in 129 deployment.program().functions().values().zip_eq(deployment.verifying_keys()) 130 { 131 // Initialize a burner private key. 132 let burner_private_key = PrivateKey::new(rng)?; 133 // Compute the burner address. 134 let burner_address = Address::try_from(&burner_private_key)?; 135 // Retrieve the input types. 136 let input_types = function.input_types(); 137 // Retrieve the program checksum, if the program has a constructor. 138 let program_checksum = match self.program().contains_constructor() { 139 true => Some(self.program_checksum_as_field()?), 140 false => None, 141 }; 142 // Sample the inputs. 143 let inputs = input_types 144 .iter() 145 .map(|input_type| match input_type { 146 ValueType::ExternalRecord(locator) => { 147 // Retrieve the external stack. 148 let stack = self.get_external_stack(locator.program_id())?; 149 // Sample the input. 150 stack.sample_value( 151 &burner_address, 152 &ValueType::Record(*locator.resource()).into(), 153 &mut seeded_rng, 154 ) 155 } 156 _ => self.sample_value(&burner_address, &input_type.into(), &mut seeded_rng), 157 }) 158 .collect::<Result<Vec<_>>>()?; 159 lap!(timer, "Sample the inputs"); 160 // Sample a dummy 'is_root'. 161 let is_root = true; 162 163 // Compute the request, with a burner private key. 164 let request = Request::sign( 165 &burner_private_key, 166 *program_id, 167 *function.name(), 168 inputs.into_iter(), 169 &input_types, 170 root_tvk, 171 is_root, 172 program_checksum, 173 rng, 174 )?; 175 lap!(timer, "Compute the request for {}", function.name()); 176 // Initialize the assignments. 177 let assignments = Assignments::<N>::default(); 178 // Initialize the constraint limit. Account for the constraint added after synthesis that makes the Varuna zerocheck hiding. 179 let Some(constraint_limit) = verifying_key.circuit_info.num_constraints.checked_sub(1) else { 180 // Since a deployment must always pay non-zero fee, it must always have at least one constraint. 181 bail!("The constraint limit of 0 for function '{}' is invalid", function.name()); 182 }; 183 // Retrieve the variable limit. 184 let variable_limit = verifying_key.num_variables(); 185 // Initialize the call stack. 186 let call_stack = CallStack::CheckDeployment( 187 vec![request], 188 burner_private_key, 189 assignments.clone(), 190 Some(constraint_limit as u64), 191 Some(variable_limit), 192 ); 193 // Append the function name, callstack, and assignments. 194 call_stacks.push((function.name(), call_stack, assignments)); 195 } 196 197 // Verify the certificates. 198 let rngs = (0..call_stacks.len()).map(|_| StdRng::from_seed(seeded_rng.r#gen())).collect::<Vec<_>>(); 199 cfg_into_iter!(call_stacks).zip_eq(deployment.verifying_keys()).zip_eq(rngs).try_for_each( 200 |(((function_name, call_stack, assignments), (_, (verifying_key, certificate))), mut rng)| { 201 // Synthesize the circuit. 202 if let Err(err) = self.execute_function::<A, _>(call_stack, caller, root_tvk, &mut rng) { 203 bail!("Failed to synthesize the circuit for '{function_name}': {err}") 204 } 205 // Check the certificate. 206 match assignments.read().last() { 207 None => bail!("The assignment for function '{function_name}' is missing in '{program_id}'"), 208 Some((assignment, _metrics)) => { 209 // Ensure the certificate is valid. 210 if !certificate.verify(&function_name.to_string(), assignment, verifying_key) { 211 bail!("The certificate for function '{function_name}' is invalid in '{program_id}'") 212 } 213 } 214 }; 215 Ok(()) 216 }, 217 )?; 218 219 finish!(timer); 220 221 Ok(()) 222 } 223 }