Skip to content

Commit

Permalink
Merge pull request #1270 from AleoHQ/feat/time-synthesizer
Browse files Browse the repository at this point in the history
Add `timers` for `VM`, `Process`, and `Stack` operations
  • Loading branch information
howardwu committed Nov 17, 2022
2 parents 6751e5a + 8ac2c57 commit 25c575c
Show file tree
Hide file tree
Showing 15 changed files with 242 additions and 14 deletions.
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
8 changes: 8 additions & 0 deletions synthesizer/src/process/stack/authorize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ impl<N: Network> Stack<N> {
inputs: impl ExactSizeIterator<Item = impl TryInto<Value<N>>>,
rng: &mut R,
) -> Result<Authorization<N>> {
let timer = timer!("Stack::authorize");

// Ensure the program contains functions.
ensure!(!self.program.functions().is_empty(), "Program '{}' has no functions", self.program.id());

Expand All @@ -44,15 +46,21 @@ impl<N: Network> Stack<N> {
input_types.len()
)
}
lap!(timer, "Verify the number of inputs");

// Compute the request.
let request = Request::sign(private_key, *self.program.id(), function_name, inputs, &input_types, rng)?;
lap!(timer, "Compute the request");
// Initialize the authorization.
let authorization = Authorization::new(&[request.clone()]);
// Construct the call stack.
let call_stack = CallStack::Authorize(vec![request], *private_key, authorization.clone());
// Construct the authorization from the function.
let _response = self.execute_function::<A, R>(call_stack, rng)?;
lap!(timer, "Construct the authorization from the function");

finish!(timer);

// Return the authorization.
Ok(authorization)
}
Expand Down

0 comments on commit 25c575c

Please sign in to comment.