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

feat: DSL recursion changes #383

Closed
wants to merge 48 commits into from
Closed

feat: DSL recursion changes #383

wants to merge 48 commits into from

Conversation

vezenovm
Copy link
Contributor

@vezenovm vezenovm commented Apr 25, 2023

Description

This PR enables the starting skeleton for recursion in Noir.

There were heavy merge conflicts between zw/noir-recursion and master. I originally made this branch mv/recursion-master-merge in this PR: #381, but it included all the changes from master in the diff. This branches checks out zw/noir-recursion and brought it up-to-date with master. The diff should now represent only the recursion changes.

There was a change in the recursion work that removed the reference string from the std lib verification key which required usage of the env crs. All the stdlib recursion tests now use the env crs. However, in the solidity tests using the env crs resulted in an error that we were trying to invert zero in the field. As a quick workaround I added back the reference string field to the stdlib verification key.

Checklist:

  • I have reviewed my diff in github, line by line.
  • Every change is related to the PR description.
  • I have linked this pull request to the issue(s) that it resolves.
  • There are no unexpected formatting changes, superfluous debug logs, or commented-out code.
  • There are no circuit changes, OR specifications in /markdown/specs have been updated.
  • There are no circuit changes, OR a cryptographer has been assigned for review.
  • I've updated any terraform that needs updating (e.g. environment variables) for deployment.
  • The branch has been rebased against the head of its merge target.
  • I'm happy for the PR to be merged at the reviewer's next convenience.
  • New functions, classes, etc. have been documented according to the doxygen comment format. Classes and structs must have @brief describing the intended functionality.
  • If existing code has been modified, such documentation has been added or updated.

zac-williamson and others added 15 commits April 13, 2023 17:29
recursive verification key no longer encapsulates a reference string

(neither were fundamentally needed!)
Added passing serialization tests

Fixed format of RecursionConstraint to be compatible with existing ACIR formatting
Can be used by backend to force recursive verification key to be a specific value (it is represented via witnesses and therefore is not by-default constraint to represent a specific circuit)
* merge master in zw/noir-recursion

* add dsl files to commit

* namespace stuff and debugging

* c bind functions

* constraint test and comment removal

* revert some changes to RecursionConstraint while debugging serialization issues

* dispaly error when trying to use create_circuit_with_witness

* is_recursive flag as part of new_proof and verify_proof, new RecursiveProver type in dsl, and serialization changes for recursion acir_proofs methods

* remove debug output from TestSerializationWithRecursion
@vezenovm vezenovm mentioned this pull request Apr 25, 2023
11 tasks
@vezenovm vezenovm marked this pull request as ready for review May 1, 2023 16:33
…ook so not caught earlier, but need more clarity on ubuntu
Copy link
Collaborator

@codygunton codygunton left a comment

Choose a reason for hiding this comment

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

OK, I've reviewed everything not in the directories dsl or solidity_helpers. Most of what I have to say are little comments for clarifiation / disucssion and thoughts for improvements that could be pursued later.

I think I noticed one constraint that goes away, and it would be good to fix this by adding the appropriate constraint in the from_witness verification key constructor. It's debatable whether that's actually a good idea -- I think it's probably ideal to keep as much of the recursive verifier logic as possible in one place (for security analysis we don't want branching in the space of circuits we create--hard to keep track of all of the possibilities). But, on that note,

UltraPlonK must not be considered secure we audit it

and our goal is to get something performant into developers' hands now, so it's certainly reasonable to move things around as you have and address the missing constraint in some way.

cpp/src/barretenberg/env/CMakeLists.txt Outdated Show resolved Hide resolved
cpp/src/barretenberg/stdlib/encryption/ecdsa/ecdsa.hpp Outdated Show resolved Hide resolved
@@ -325,18 +343,14 @@ aggregation_state<Curve> verify_proof(typename Curve::Composer* context,
rhs_scalars.push_back(random_separator);
}

// Check if recursive proof information is correctly set.
key->contains_recursive_proof.assert_equal(key->base_key->contains_recursive_proof,
Copy link
Collaborator

Choose a reason for hiding this comment

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

⚠️ it looks like this logic was moved into from_field_pt_vector, but if we use the from_witness of a verification constructor of a verification key we will be missing this constraint!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@zac-williamson This is the constraint that was removed

Copy link
Contributor

Choose a reason for hiding this comment

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

In the new from_field_pt_vector, key->contains_recursive_proof is a circuit constant, it's not a witness (it's value is constructed out of a regular bool type)

The constraint is removed because there is no longer a contains_recursive_proof witness value at all - we effectively generate 2 different circuits depending on whether the input into from_field_pt_vector describes a recursive circuit or not.

@codygunton
Copy link
Collaborator

@suyash67 I recommend having a look here to see if any of the recursive verifier changes will affect you. But note that we will get some feedback from aztec tests once @vezenovm rebases 😇

// (N.B. up to the ACIR description to make sure that the final output aggregation object wires are public
// inputs!)
if (i == constraint_system.recursion_constraints.size() - 1) {
std::vector<uint32_t> proof_output_witness_indices(constraint.output_aggregation_object.begin(),
Copy link
Contributor

@zac-williamson zac-williamson May 22, 2023

Choose a reason for hiding this comment

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

I added a method into aggregation_state that unifies these two steps (populating witness indices and calling set_recursive_proof): void aggregation_state::assign_object_to_proof_outputs

Would you mind updating to use it? I think it would allows us to remove the set_recursive_proof method and remove the complexity of having to assign proof_output_witness_indices

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It does look like we may still have to assign proof_output_witness_indices as I have to construct an aggregation_state in order to use assign_object_to_proof_outputs. It also looks like constructing the aggregation state will require fetching all the values of the output_aggregation_state from the composer to construct P0 and P1. I can still of course push an update to show how it would look but it does seem that there is some extra complexity to using assign_object_to_proof_outputs even if we can get rid of set_recursive_proof

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh that is a good point. One alternative is to pass in a bool add_to_public_inputs parameter into create_recursion_constraint. If add_to_public_inputs == true then we call assign_object_to_proof_outputs on aggregation_state_ct result (line 116)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately that alternative looks to also complicate how we currently construct our constraints. Calling result.assign_object_proof_outputs would mean we are now including the output aggregation object on the result aggregation state directly rather than it being a separate field on our RecursionConstraint. This change would require some larger changes in how we construct our recursion constraints and should be left as a separate optimization.

Copy link
Contributor

Choose a reason for hiding this comment

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

sounds good. Might be a good idea to create a GH Issue to refactor the AggregationState interface to more easily handle this case + regular recursion w/o exposing internals like proof_output_witness_indices

const auto element = barretenberg::g1::affine_element(barretenberg::g1::one * scalar);
const uint256_t x = element.x;
const uint256_t y = element.y;
const barretenberg::fr x_lo = x.slice(0, 136);
Copy link
Contributor

Choose a reason for hiding this comment

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

Could we replace these magic numbers with derived values? These come from proof_system::plonk::NUM_LIMB_BITS_IN_FIELD_SIMULATION (68)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Made a new function export_g1_affine_element_as_fields that uses constexprs to determine the limb slices

}
}

template void create_recursion_constraints<false>(Composer&, const RecursionConstraint&);
Copy link
Contributor

Choose a reason for hiding this comment

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

This differs from how it is done for ecdsa_constraints -- we should add dummy_constraints in one way

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll switch from template to a flag

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Switched

std::vector<acir_format::field_ct> proof_fields(proof_length / 32);
std::vector<acir_format::field_ct> key_fields(vk_length / 32);
for (size_t i = 0; i < proof_length / 32; i++) {
// TODO(maxim): The stdlib pairing primitives fetch the context from the elements being used in the pairing
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Issue for this TODO can be found here: #447

@Savio-Sou
Copy link
Member

Superseded by #485.

@Savio-Sou Savio-Sou closed this Jun 12, 2023
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.

5 participants