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

Refactor CallStack::Synthesize to produce consistent dummy outputs for faster nested import deployments #2598

Open
wants to merge 8 commits into
base: staging
Choose a base branch
from

Conversation

kpandl
Copy link

@kpandl kpandl commented Jan 17, 2025

Summary

This PR refactors the way CallStack::Synthesize is run, decoupling the Synthesize mode from the old execute_function approach. It produces consistent dummy outputs (inspired by CheckDeployment) more rapidly and skips sub-circuit construction for nested import calls. The result is significantly faster deployments for deeply nested imports, while still generating cryptographically coherent record nonces.

Motivation

Previously, Authorize and Synthesize modes were lumped together. As such, calls in CallStack::Synthesize were forced to run a real sub-circuit via execute_function. This is slow for nested imports and has led to an exponential growth in deployment time w.r.t. the import depth.

Thus, this PR refactors the Synthesize branch to skip full circuit execution and produce “dummy” outputs with consistent record nonces.

Note

This PR changes expected future IDs in 3 of the execute_and_finalize tests. The reason is that previously, Synthesize always ran real sub-circuits, consuming RNG draws. With this PR, certain sub-circuits are skipped by producing dummy outputs. As a result, the RNG is executed less times, leading to different transaction commitments and future IDs.

Test Plan

Tested the deployment and execution of programs with deep imports in a local devnet.

Related PRs

@vicsn
Copy link
Collaborator

vicsn commented Jan 20, 2025

NOTE: this PR will also significantly speed up tests using nested deployments, e.g. test_deep_nested_execution_cost.

@@ -45,7 +45,7 @@ pub trait CallTrait<N: Network> {
/// Executes the instruction.
fn execute<A: circuit::Aleo<Network = N>, R: CryptoRng + Rng>(
&self,
stack: &(impl StackEvaluate<N> + StackExecute<N> + StackMatches<N> + StackProgram<N>),
stack: &Stack<N>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change the interface? Can we keep it the way it was? Same for the other function.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed here: 774efae

@@ -316,6 +316,12 @@ impl<N: Network> StackProgram<N> for Stack<N> {
.ok_or_else(|| anyhow!("Function '{function_name}' does not exist"))
}

/// Returns true if the proving key for the given function name already exists in this stack.
#[inline]
fn has_proving_key(&self, function_name: &Identifier<N>) -> bool {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why add this extra wrapper?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if we don't want to have this wrapper, I need to revert 774efae.
I guess the cleanest solution is if I go ahead and to that, so we can directly call contains_proving_key?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see. Hmm I don't know the reasoning behind the choice of StackProgram<N> trait existing. But let's try to keep that unchanged.

One approach which could be somewhat consistent, is to create a new trait StackKeys. And these particular functions (contains/get/insert proving/verifying keys) could impl<N> StackKeys<N>.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed here: 6ebe642

// Return the request and response.
(request, response)
}
CallStack::Synthesize(_, private_key, ..) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing comment like you added to the other synthesize and authorize cases

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any difference to the contents of CallStack::Synthesize and CallStack::CheckDeployment? Perhaps they can be merged into the same case.

Copy link
Author

@kpandl kpandl Jan 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed both here: 815a4df
Synthesize and CheckDeployment are indeed the same!

Comment on lines 312 to 316
// Compute the randomizer.
let randomizer = N::hash_to_scalar_psd2(&[*request.tvk(), index])?;
// Construct the record nonce from that randomizer.
let record_nonce = N::g_scalar_multiply(&randomizer);
// Sample the record with that nonce.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be made a part of sample_record, so that callers of sample_record don't have to think about it anymore and all are guaranteed correct usage?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created a new function sample_record_using_tvk here: 0503e5c

@kpandl kpandl requested a review from vicsn January 21, 2025 17:16
@@ -265,22 +267,24 @@ impl<N: Network> CallTrait<N> for Call<N> {
inputs.iter(),
&function.input_types(),
root_tvk,
is_root,
false,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use false?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed here: d7070d9

@@ -289,38 +293,38 @@ impl<N: Network> CallTrait<N> for Call<N> {
inputs.iter(),
&function.input_types(),
root_tvk,
is_root,
false,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use false?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed here: d7070d9

@kpandl kpandl requested a review from vicsn January 23, 2025 16:25
@kpandl
Copy link
Author

kpandl commented Jan 23, 2025

note: the failing snarkvm CI test is a CI/CDN issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants