use super::*;
impl<N: Network> Stack<N> {
#[inline]
pub fn deploy<A: circuit::Aleo<Network = N>, R: Rng + CryptoRng>(&self, rng: &mut R) -> Result<Deployment<N>> {
ensure!(!self.program.functions().is_empty(), "Program '{}' has no functions", self.program.id());
let mut bundle = IndexMap::with_capacity(self.program.functions().len());
for function_name in self.program.functions().keys() {
self.synthesize_key::<A, R>(function_name, rng)?;
let proving_key = self.get_proving_key(function_name)?;
let verifying_key = self.get_verifying_key(function_name)?;
let certificate = Certificate::certify(function_name, &proving_key, &verifying_key)?;
bundle.insert(*function_name, (verifying_key, certificate));
}
Deployment::new(N::EDITION, self.program.clone(), bundle)
}
#[inline]
pub fn verify_deployment<A: circuit::Aleo<Network = N>, R: Rng + CryptoRng>(
&self,
deployment: &Deployment<N>,
rng: &mut R,
) -> Result<()> {
let edition = deployment.edition();
let program = &self.program;
let program_id = program.id();
let verifying_keys = deployment.verifying_keys();
ensure!(edition == N::EDITION, "Deployed the wrong edition (expected '{}', found '{edition}').", N::EDITION);
ensure!(program == deployment.program(), "The stack program does not match the deployment program");
ensure!(program_id.is_aleo(), "Program '{program_id}' has an incorrect network-level domain (NLD)");
ensure!(!program.functions().is_empty(), "No functions present in the deployment for program '{program_id}'");
ensure!(!verifying_keys.is_empty(), "No verifying keys present in the deployment for program '{program_id}'");
if verifying_keys.len() != program.functions().len() {
bail!("The number of verifying keys does not match the number of program functions");
}
for ((function_name, function), candidate_name) in program.functions().iter().zip_eq(verifying_keys.keys()) {
if function_name != function.name() {
bail!("The function key is '{function_name}', but the function name is '{}'", function.name())
}
if candidate_name != function.name() {
bail!("The verifier key is '{candidate_name}', but the function name is '{}'", function.name())
}
}
for (function, (verifying_key, certificate)) in program.functions().values().zip_eq(verifying_keys.values()) {
let burner_private_key = PrivateKey::new(rng)?;
let burner_address = Address::try_from(&burner_private_key)?;
let input_types = function.input_types();
let inputs = input_types
.iter()
.map(|input_type| match input_type {
ValueType::ExternalRecord(locator) => {
let stack = self.get_external_stack(locator.program_id())?;
stack.sample_value(&burner_address, &ValueType::Record(*locator.resource()), rng)
}
_ => self.sample_value(&burner_address, input_type, rng),
})
.collect::<Result<Vec<_>>>()?;
let request =
Request::sign(&burner_private_key, *program_id, *function.name(), &inputs, &input_types, rng)?;
let assignments = Assignments::<N>::default();
let call_stack = CallStack::CheckDeployment(vec![request], burner_private_key, assignments.clone());
let _response = self.execute_function::<A, R>(call_stack, rng)?;
match assignments.read().last() {
None => bail!("The assignment for function '{}' is missing in '{program_id}'", function.name()),
Some(assignment) => {
if !certificate.verify(function.name(), assignment, verifying_key) {
bail!("The certificate for function '{}' is invalid in '{program_id}'", function.name())
}
}
};
}
Ok(())
}
}