snarkvm_synthesizer_process/
lib.rs#![forbid(unsafe_code)]
#![allow(clippy::too_many_arguments)]
#![allow(clippy::type_complexity)]
mod cost;
pub use cost::*;
mod stack;
pub use stack::*;
mod trace;
pub use trace::*;
mod traits;
pub use traits::*;
mod authorize;
mod deploy;
mod evaluate;
mod execute;
mod finalize;
mod verify_deployment;
mod verify_execution;
mod verify_fee;
#[cfg(test)]
mod tests;
use console::{
account::PrivateKey,
network::prelude::*,
program::{Identifier, Literal, Locator, Plaintext, ProgramID, Record, Response, Value, compute_function_id},
types::{Field, U16, U64},
};
use ledger_block::{Deployment, Execution, Fee, Input, Output, Transition};
use ledger_store::{FinalizeStorage, FinalizeStore, atomic_batch_scope};
use synthesizer_program::{
Branch,
Closure,
Command,
Finalize,
FinalizeGlobalState,
FinalizeOperation,
Instruction,
Program,
RegistersLoad,
RegistersStore,
StackProgram,
};
use synthesizer_snark::{ProvingKey, UniversalSRS, VerifyingKey};
use aleo_std::prelude::{finish, lap, timer};
use indexmap::IndexMap;
use parking_lot::RwLock;
use std::{collections::HashMap, sync::Arc};
#[cfg(feature = "aleo-cli")]
use colored::Colorize;
#[derive(Clone)]
pub struct Process<N: Network> {
universal_srs: Arc<UniversalSRS<N>>,
stacks: IndexMap<ProgramID<N>, Arc<Stack<N>>>,
}
impl<N: Network> Process<N> {
#[inline]
pub fn setup<A: circuit::Aleo<Network = N>, R: Rng + CryptoRng>(rng: &mut R) -> Result<Self> {
let timer = timer!("Process:setup");
let mut process = Self { universal_srs: Arc::new(UniversalSRS::load()?), stacks: IndexMap::new() };
lap!(timer, "Initialize process");
let program = Program::credits()?;
lap!(timer, "Load credits program");
let stack = Stack::new(&process, &program)?;
lap!(timer, "Initialize stack");
for function_name in program.functions().keys() {
stack.synthesize_key::<A, _>(function_name, rng)?;
lap!(timer, "Synthesize circuit keys for {function_name}");
}
lap!(timer, "Synthesize credits program keys");
process.add_stack(stack);
finish!(timer);
Ok(process)
}
#[inline]
pub fn add_program(&mut self, program: &Program<N>) -> Result<()> {
let credits_program_id = ProgramID::<N>::from_str("credits.aleo")?;
if program.id() != &credits_program_id {
self.add_stack(Stack::new(self, program)?);
}
Ok(())
}
#[inline]
pub fn add_stack(&mut self, stack: Stack<N>) {
self.stacks.insert(*stack.program_id(), Arc::new(stack));
}
}
impl<N: Network> Process<N> {
#[inline]
pub fn load() -> Result<Self> {
let timer = timer!("Process::load");
let mut process = Self { universal_srs: Arc::new(UniversalSRS::load()?), stacks: IndexMap::new() };
lap!(timer, "Initialize process");
let program = Program::credits()?;
lap!(timer, "Load credits program");
let stack = Stack::new(&process, &program)?;
lap!(timer, "Initialize stack");
for function_name in program.functions().keys() {
let verifying_key = N::get_credits_verifying_key(function_name.to_string())?;
let num_variables = verifying_key.circuit_info.num_public_and_private_variables as u64;
stack.insert_verifying_key(function_name, VerifyingKey::new(verifying_key.clone(), num_variables))?;
lap!(timer, "Load verifying key for {function_name}");
}
lap!(timer, "Load circuit keys");
process.add_stack(stack);
finish!(timer, "Process::load");
Ok(process)
}
#[inline]
#[cfg(feature = "wasm")]
pub fn load_web() -> Result<Self> {
let mut process = Self { universal_srs: Arc::new(UniversalSRS::load()?), stacks: IndexMap::new() };
let program = Program::credits()?;
let stack = Stack::new(&process, &program)?;
process.add_stack(stack);
Ok(process)
}
#[inline]
pub const fn universal_srs(&self) -> &Arc<UniversalSRS<N>> {
&self.universal_srs
}
#[inline]
pub fn contains_program(&self, program_id: &ProgramID<N>) -> bool {
self.stacks.contains_key(program_id)
}
#[inline]
pub fn get_stack(&self, program_id: impl TryInto<ProgramID<N>>) -> Result<&Arc<Stack<N>>> {
let program_id = program_id.try_into().map_err(|_| anyhow!("Invalid program ID"))?;
let stack = self.stacks.get(&program_id).ok_or_else(|| anyhow!("Program '{program_id}' does not exist"))?;
ensure!(stack.program_id() == &program_id, "Expected program '{}', found '{program_id}'", stack.program_id());
Ok(stack)
}
#[inline]
pub fn get_program(&self, program_id: impl TryInto<ProgramID<N>>) -> Result<&Program<N>> {
Ok(self.get_stack(program_id)?.program())
}
#[inline]
pub fn get_proving_key(
&self,
program_id: impl TryInto<ProgramID<N>>,
function_name: impl TryInto<Identifier<N>>,
) -> Result<ProvingKey<N>> {
let function_name = function_name.try_into().map_err(|_| anyhow!("Invalid function name"))?;
self.get_stack(program_id)?.get_proving_key(&function_name)
}
#[inline]
pub fn get_verifying_key(
&self,
program_id: impl TryInto<ProgramID<N>>,
function_name: impl TryInto<Identifier<N>>,
) -> Result<VerifyingKey<N>> {
let function_name = function_name.try_into().map_err(|_| anyhow!("Invalid function name"))?;
self.get_stack(program_id)?.get_verifying_key(&function_name)
}
#[inline]
pub fn insert_proving_key(
&self,
program_id: &ProgramID<N>,
function_name: &Identifier<N>,
proving_key: ProvingKey<N>,
) -> Result<()> {
self.get_stack(program_id)?.insert_proving_key(function_name, proving_key)
}
#[inline]
pub fn insert_verifying_key(
&self,
program_id: &ProgramID<N>,
function_name: &Identifier<N>,
verifying_key: VerifyingKey<N>,
) -> Result<()> {
self.get_stack(program_id)?.insert_verifying_key(function_name, verifying_key)
}
#[inline]
pub fn synthesize_key<A: circuit::Aleo<Network = N>, R: Rng + CryptoRng>(
&self,
program_id: &ProgramID<N>,
function_name: &Identifier<N>,
rng: &mut R,
) -> Result<()> {
self.get_stack(program_id)?.synthesize_key::<A, R>(function_name, rng)
}
}
#[cfg(test)]
pub mod test_helpers {
use super::*;
use console::{account::PrivateKey, network::MainnetV0, program::Identifier};
use ledger_block::Transition;
use ledger_query::Query;
use ledger_store::{BlockStore, helpers::memory::BlockMemory};
use synthesizer_program::Program;
use once_cell::sync::OnceCell;
type CurrentNetwork = MainnetV0;
type CurrentAleo = circuit::network::AleoV0;
pub fn get_execution(
process: &mut Process<CurrentNetwork>,
program: &Program<CurrentNetwork>,
function_name: &Identifier<CurrentNetwork>,
inputs: impl ExactSizeIterator<Item = impl TryInto<Value<CurrentNetwork>>>,
) -> Execution<CurrentNetwork> {
let rng = &mut TestRng::default();
let private_key = PrivateKey::new(rng).unwrap();
if !process.contains_program(program.id()) {
process.add_program(program).unwrap();
}
let authorization =
process.authorize::<CurrentAleo, _>(&private_key, program.id(), function_name, inputs, rng).unwrap();
let (_, mut trace) = process.execute::<CurrentAleo, _>(authorization, rng).unwrap();
let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(None).unwrap();
trace.prepare(ledger_query::Query::from(block_store)).unwrap();
let locator = format!("{:?}:{function_name:?}", program.id());
trace.prove_execution::<CurrentAleo, _>(&locator, rng).unwrap()
}
pub fn sample_key() -> (Identifier<CurrentNetwork>, ProvingKey<CurrentNetwork>, VerifyingKey<CurrentNetwork>) {
static INSTANCE: OnceCell<(
Identifier<CurrentNetwork>,
ProvingKey<CurrentNetwork>,
VerifyingKey<CurrentNetwork>,
)> = OnceCell::new();
INSTANCE
.get_or_init(|| {
let (string, program) = Program::<CurrentNetwork>::parse(
r"
program testing.aleo;
function compute:
input r0 as u32.private;
input r1 as u32.public;
add r0 r1 into r2;
output r2 as u32.public;",
)
.unwrap();
assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
let function_name = Identifier::from_str("compute").unwrap();
let rng = &mut TestRng::default();
let process = sample_process(&program);
process.synthesize_key::<CurrentAleo, _>(program.id(), &function_name, rng).unwrap();
let proving_key = process.get_proving_key(program.id(), function_name).unwrap();
let verifying_key = process.get_verifying_key(program.id(), function_name).unwrap();
(function_name, proving_key, verifying_key)
})
.clone()
}
pub(crate) fn sample_execution() -> Execution<CurrentNetwork> {
static INSTANCE: OnceCell<Execution<CurrentNetwork>> = OnceCell::new();
INSTANCE
.get_or_init(|| {
let (string, program) = Program::<CurrentNetwork>::parse(
r"
program testing.aleo;
function compute:
input r0 as u32.private;
input r1 as u32.public;
add r0 r1 into r2;
output r2 as u32.public;",
)
.unwrap();
assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
let function_name = Identifier::from_str("compute").unwrap();
let rng = &mut TestRng::default();
let caller_private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
let block_store = BlockStore::<CurrentNetwork, BlockMemory<_>>::open(None).unwrap();
let process = sample_process(&program);
let authorization = process
.authorize::<CurrentAleo, _>(
&caller_private_key,
program.id(),
function_name,
["5u32", "10u32"].into_iter(),
rng,
)
.unwrap();
assert_eq!(authorization.len(), 1);
let (_response, mut trace) = process.execute::<CurrentAleo, _>(authorization, rng).unwrap();
assert_eq!(trace.transitions().len(), 1);
trace.prepare(Query::from(block_store)).unwrap();
trace.prove_execution::<CurrentAleo, _>("testing", rng).unwrap()
})
.clone()
}
pub fn sample_transition() -> Transition<CurrentNetwork> {
let mut execution = sample_execution();
assert!(!execution.is_empty());
execution.pop().unwrap()
}
pub(crate) fn sample_process(program: &Program<CurrentNetwork>) -> Process<CurrentNetwork> {
let mut process = Process::load().unwrap();
process.add_program(program).unwrap();
process
}
}