Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add timers for VM, Process, and Stack operations #1270

Merged
merged 7 commits into from
Nov 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 15 additions & 10 deletions synthesizer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ path = "benches/coinbase_puzzle.rs"
harness = false
required-features = [ "setup" ]

[features]
default = [ "parallel" ]
parallel = [
"rayon",
"snarkvm-fields/parallel",
"snarkvm-utilities/parallel"
]
aleo-cli = [ ]
setup = [ ]
timer = [ "aleo-std/timer" ]

[dependencies.circuit]
package = "snarkvm-circuit"
path = "../circuit"
Expand Down Expand Up @@ -61,6 +72,10 @@ path = "../utilities"
version = "0.9.7"
default-features = false

[dependencies.aleo-std]
version = "0.1.15"
default-features = false

[dependencies.anyhow]
version = "1.0.66"

Expand Down Expand Up @@ -114,13 +129,3 @@ version = "1.3"

[dev-dependencies.criterion]
version = "0.4.0"

[features]
default = [ "parallel" ]
parallel = [
"rayon",
"snarkvm-fields/parallel",
"snarkvm-utilities/parallel"
]
aleo-cli = [ ]
setup = [ ]
39 changes: 37 additions & 2 deletions synthesizer/src/process/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,19 @@ impl<N: Network> Process<N> {
program: &Program<N>,
rng: &mut R,
) -> Result<Deployment<N>> {
let timer = timer!("Process::deploy");

// Compute the stack.
let stack = Stack::new(self, program)?;
lap!(timer, "Compute the stack");

// Return the deployment.
stack.deploy::<A, R>(rng)
let deployment = stack.deploy::<A, R>(rng);
lap!(timer, "Construct the deployment");

finish!(timer);

deployment
}

/// Verifies the given deployment is well-formed.
Expand All @@ -37,14 +46,22 @@ impl<N: Network> Process<N> {
deployment: &Deployment<N>,
rng: &mut R,
) -> Result<()> {
let timer = timer!("Process::verify_deployment");
// Retrieve the program ID.
let program_id = deployment.program().id();
// Ensure the program does not already exist in the process.
ensure!(!self.contains_program(program_id), "Program '{program_id}' already exists");
// Ensure the program is well-formed, by computing the stack.
let stack = Stack::new(self, deployment.program())?;
lap!(timer, "Compute the stack");

// Ensure the verifying keys are well-formed and the certificates are valid.
stack.verify_deployment::<A, R>(deployment, rng)
let verification = stack.verify_deployment::<A, R>(deployment, rng);
lap!(timer, "Verify the deployment");

finish!(timer);

verification
}

/// Finalizes the deployment.
Expand All @@ -55,40 +72,58 @@ impl<N: Network> Process<N> {
store: &ProgramStore<N, P>,
deployment: &Deployment<N>,
) -> Result<()> {
let timer = timer!("Process::finalize_deployment");

// TODO (howardwu): Make this function atomic.
// TODO (howardwu): Check the program ID and all mappings don't exist in the 'store'. (add this to verify_deployment too)

// Compute the program stack.
let stack = Stack::new(self, deployment.program())?;
lap!(timer, "Compute the stack");

// Insert the verifying keys.
for (function_name, (verifying_key, _)) in deployment.verifying_keys() {
stack.insert_verifying_key(function_name, verifying_key.clone())?;
}
lap!(timer, "Insert the verifying keys");

// Retrieve the program ID.
let program_id = deployment.program_id();
// Iterate through the program mappings.
for mapping in deployment.program().mappings().values() {
store.initialize_mapping(program_id, mapping.name())?;
}
lap!(timer, "Initialize the program mappings");

// Add the stack to the process.
self.stacks.insert(*deployment.program_id(), stack);

finish!(timer);

Ok(())
}

/// Adds the newly-deployed program.
/// This method assumes the given deployment **is valid**.
#[inline]
pub(crate) fn load_deployment(&mut self, deployment: &Deployment<N>) -> Result<()> {
let timer = timer!("Process::load_deployment");

// Compute the program stack.
let stack = Stack::new(self, deployment.program())?;
lap!(timer, "Compute the stack");

// Insert the verifying keys.
for (function_name, (verifying_key, _)) in deployment.verifying_keys() {
stack.insert_verifying_key(function_name, verifying_key.clone())?;
}
lap!(timer, "Insert the verifying keys");

// Add the stack to the process.
self.stacks.insert(*deployment.program_id(), stack);

finish!(timer);

Ok(())
}
}
Expand Down
10 changes: 9 additions & 1 deletion synthesizer/src/process/evaluate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,21 @@ impl<N: Network> Process<N> {
/// Evaluates a program function on the given request.
#[inline]
pub fn evaluate<A: circuit::Aleo<Network = N>>(&self, authorization: Authorization<N>) -> Result<Response<N>> {
let timer = timer!("Process::evaluate");

// Retrieve the main request (without popping it).
let request = authorization.peek_next()?;

#[cfg(feature = "aleo-cli")]
println!("{}", format!(" • Evaluating '{}/{}'...", request.program_id(), request.function_name()).dimmed());

// Evaluate the function.
self.get_stack(request.program_id())?.evaluate_function::<A>(CallStack::evaluate(authorization)?)
let response =
self.get_stack(request.program_id())?.evaluate_function::<A>(CallStack::evaluate(authorization)?);
lap!(timer, "Evaluate the function");

finish!(timer);

response
}
}
23 changes: 23 additions & 0 deletions synthesizer/src/process/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ impl<N: Network> Process<N> {
authorization: Authorization<N>,
rng: &mut R,
) -> Result<(Response<N>, Execution<N>, Inclusion<N>)> {
let timer = timer!("Process::execute");

// Retrieve the main request (without popping it).
let request = authorization.peek_next()?;

Expand All @@ -36,22 +38,27 @@ impl<N: Network> Process<N> {
let inclusion = Arc::new(RwLock::new(Inclusion::new()));
// Initialize the call stack.
let call_stack = CallStack::execute(authorization, execution.clone(), inclusion.clone())?;
lap!(timer, "Initialize call stack");
// Execute the circuit.
let response = self.get_stack(request.program_id())?.execute_function::<A, R>(call_stack, rng)?;
lap!(timer, "Execute the function");
// Extract the execution.
let execution = Arc::try_unwrap(execution).unwrap().into_inner();
// Ensure the execution is not empty.
ensure!(!execution.is_empty(), "Execution of '{}/{}' is empty", request.program_id(), request.function_name());
// Extract the inclusion.
let inclusion = Arc::try_unwrap(inclusion).unwrap().into_inner();

finish!(timer);
Ok((response, execution, inclusion))
}

/// Verifies the given execution is valid.
/// Note: This does *not* check that the global state root exists in the ledger.
#[inline]
pub fn verify_execution<const VERIFY_INCLUSION: bool>(&self, execution: &Execution<N>) -> Result<()> {
let timer = timer!("Process::verify_execution");

// Ensure the execution contains transitions.
ensure!(!execution.is_empty(), "There are no transitions in the execution");

Expand All @@ -69,10 +76,12 @@ impl<N: Network> Process<N> {
execution.len()
);
}
lap!(timer, "Verify the number of transitions");

// Ensure the inclusion proof is valid.
if VERIFY_INCLUSION {
Inclusion::verify_execution(execution)?;
lap!(timer, "Verify the inclusion proof");
}

// Replicate the execution stack for verification.
Expand Down Expand Up @@ -110,6 +119,8 @@ impl<N: Network> Process<N> {
{
bail!("Failed to verify a transition input")
}
lap!(timer, "Verify the inputs");

// Ensure each output is valid.
let num_inputs = transition.inputs().len();
if transition
Expand All @@ -120,6 +131,7 @@ impl<N: Network> Process<N> {
{
bail!("Failed to verify a transition output")
}
lap!(timer, "Verify the outputs");

// Ensure the fee is correct.
match Program::is_coinbase(transition.program_id(), transition.function_name()) {
Expand Down Expand Up @@ -195,6 +207,7 @@ impl<N: Network> Process<N> {

// [Inputs] Extend the verifier inputs with the fee.
inputs.push(*I64::<N>::new(*transition.fee()).to_field()?);
lap!(timer, "Construct the verifier inputs");

#[cfg(debug_assertions)]
println!("Transition public inputs ({} elements): {:#?}", inputs.len(), inputs);
Expand All @@ -206,7 +219,11 @@ impl<N: Network> Process<N> {
verifying_key.verify(function.name(), &inputs, transition.proof()),
"Transition is invalid - failed to verify transition proof"
);

lap!(timer, "Verify transition proof for {}", function.name());
}

finish!(timer);
Ok(())
}

Expand All @@ -218,6 +235,8 @@ impl<N: Network> Process<N> {
store: &ProgramStore<N, P>,
execution: &Execution<N>,
) -> Result<()> {
let timer = timer!("Program::finalize_execution");

// Ensure the execution contains transitions.
ensure!(!execution.is_empty(), "There are no transitions in the execution");

Expand All @@ -235,6 +254,7 @@ impl<N: Network> Process<N> {
execution.len()
);
}
lap!(timer, "Verify the number of transitions");

// TODO (howardwu): This is a temporary approach. We should create a "CallStack" and recurse through the stack.
// Currently this loop assumes a linearly execution stack.
Expand Down Expand Up @@ -287,8 +307,11 @@ impl<N: Network> Process<N> {
registers.load(stack, &Operand::Register(register.clone()))
})
.collect::<Result<Vec<_>>>()?;

lap!(timer, "Finalize transition for {function_name}");
}
}
finish!(timer);

Ok(())
}
Expand Down
20 changes: 20 additions & 0 deletions synthesizer/src/process/execute_fee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ impl<N: Network> Process<N> {
fee_in_gates: u64,
rng: &mut R,
) -> Result<(Response<N>, Transition<N>, Inclusion<N>)> {
let timer = timer!("Process::execute_fee");

// Ensure the fee has the correct program ID.
let program_id = ProgramID::from_str("credits.aleo")?;
// Ensure the fee has the correct function.
Expand All @@ -35,14 +37,18 @@ impl<N: Network> Process<N> {
let input_types = self.get_program(program_id)?.get_function(&function_name)?.input_types();
// Construct the inputs.
let inputs = [Value::Record(credits), Value::from_str(&format!("{}", U64::<N>::new(fee_in_gates)))?];
lap!(timer, "Construct the inputs");
// Compute the request.
let request = Request::sign(private_key, program_id, function_name, inputs.iter(), &input_types, rng)?;
lap!(timer, "Compute the request");
// Initialize the authorization.
let authorization = Authorization::new(&[request.clone()]);
lap!(timer, "Initialize the authorization");
// Construct the call stack.
let call_stack = CallStack::Authorize(vec![request], *private_key, authorization.clone());
// Construct the authorization from the function.
let _response = self.get_stack(program_id)?.execute_function::<A, R>(call_stack, rng)?;
lap!(timer, "Construct the authorization from the function");

// Retrieve the main request (without popping it).
let request = authorization.peek_next()?;
Expand All @@ -60,20 +66,26 @@ impl<N: Network> Process<N> {
let call_stack = CallStack::execute(authorization, execution.clone(), inclusion.clone())?;
// Execute the circuit.
let response = stack.execute_function::<A, R>(call_stack, rng)?;
lap!(timer, "Execute the circuit");

// Extract the execution.
let execution = Arc::try_unwrap(execution).unwrap().into_inner();
// Ensure the execution contains 1 transition.
ensure!(execution.len() == 1, "Execution of '{}/{}' does not contain 1 transition", program_id, function_name);
// Extract the inclusion.
let inclusion = Arc::try_unwrap(inclusion).unwrap().into_inner();

finish!(timer);

Ok((response, execution.peek()?.clone(), inclusion))
}

/// Verifies the given fee is valid.
/// Note: This does *not* check that the global state root exists in the ledger.
#[inline]
pub fn verify_fee(&self, fee: &Fee<N>) -> Result<()> {
let timer = timer!("Process::verify_fee");

#[cfg(debug_assertions)]
println!("Verifying fee from {}/{}...", fee.program_id(), fee.function_name());

Expand Down Expand Up @@ -103,6 +115,8 @@ impl<N: Network> Process<N> {
if fee.inputs().iter().enumerate().any(|(index, input)| !input.verify(function_id, fee.tcm(), index)) {
bail!("Failed to verify a fee input")
}
lap!(timer, "Verify the inputs");

// Ensure each output is valid.
let num_inputs = fee.inputs().len();
if fee
Expand All @@ -113,12 +127,14 @@ impl<N: Network> Process<N> {
{
bail!("Failed to verify a fee output")
}
lap!(timer, "Verify the outputs");

// Ensure the fee is not negative.
ensure!(fee.fee() >= &0, "The fee must be zero or positive");

// Ensure the inclusion proof is valid.
Inclusion::verify_fee(fee)?;
lap!(timer, "Verify the inclusion proof");

// Compute the x- and y-coordinate of `tpk`.
let (tpk_x, tpk_y) = fee.tpk().to_xy_coordinates();
Expand All @@ -131,6 +147,7 @@ impl<N: Network> Process<N> {
inputs.extend(fee.outputs().iter().flat_map(|output| output.verifier_inputs()));
// Extend the inputs with the fee.
inputs.push(*I64::<N>::new(*fee.fee()).to_field()?);
lap!(timer, "Construct the verifier inputs");

// Retrieve the stack.
let stack = self.get_stack(fee.program_id())?;
Expand All @@ -157,6 +174,9 @@ impl<N: Network> Process<N> {
verifying_key.verify(function.name(), &inputs, fee.proof()),
"Fee is invalid - failed to verify transition proof"
);
lap!(timer, "Verify the transition proof");

finish!(timer);

Ok(())
}
Expand Down
1 change: 1 addition & 0 deletions synthesizer/src/process/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ use console::{
types::{I64, U16, U64},
};

use aleo_std::prelude::{finish, lap, timer};
use indexmap::IndexMap;
use parking_lot::RwLock;
#[cfg(test)]
Expand Down