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

[Fix] Fix circuit for cast lossy field to group. #2133

Merged
merged 20 commits into from
Jan 9, 2024

Conversation

acoglio
Copy link
Contributor

@acoglio acoglio commented Oct 26, 2023

This resolves the underconstraining bug that was found in that circuit.

The fix involves the creation of two new circuits, which are more general and may have other uses:

  • A circuit for square roots flagged nondeterministic, which return both square roots, in no specified order (hence 'nondeterministic'), if the input is a non-zero square, and returns an error flag set if the input is not a square.
  • A circuit for turning an x coordinate into the (unique, if it exists) point in the subgroup with that x coordinate, with an error flag set if there is no such point in the subgroup. This circuit makes use of the previous one to solve the curve equation, i.e. to find whether and where x intersects the elliptic curve.

The new circuit for cast lossy field to group is realized by using the latter circuit to attempt to find the point in the subgroup with the given value as x coordinate. If none exists, we return the generator if the input is 0, otherwise the result of Elligator-2 is returned, which is always a subgroup point.

The use of the new circuits for flagged operations eliminate the underconstraining without eliminating desired solutions (which would happen by enforcing constraints without taking flags into account).

This PR is draft because I still need to add tests for the two new circuits, but the implementation can be already reviewed.

This resolves the underconstraining bug that was found in that circuit.

The fix involves the creation of two new circuits, which are more general and
may have other uses:
- A circuit for square roots flagged nondeterministic, which return both square
  roots, in no specified order (hence 'nondeterministic'), if the input is a
  non-zero square, and returns an error flag set if the input is not a square.
- A circuit for turning an x coordinate into the (unique, if it exists) point
  in the subgroup with that x coordinate, with an error flag set if there is
  no such point in the subgroup. This circuit makes use of the previous one
  to solve the curve equation, i.e. to find whether and where x intersects
  the elliptic curve.

The new circuit for cast lossy field to group is realized by using the latter
circuit to attempt to find the point in the subgroup with the given value as x
coordinate. If none exists, we return the generator if the input is 0, otherwise
the result of Elligator-2 is returned, which is always a subgroup point.

The use of the new circuits for flagged operations eliminate the
underconstraining without eliminating desired solutions (which would happen by
enforcing constraints without taking flags into account).
@acoglio acoglio requested review from bendyarm, howardwu and d0cd October 26, 2023 22:26
@acoglio
Copy link
Contributor Author

acoglio commented Oct 30, 2023

As additional "testing", I inspected part of the constraints in the samples (generated by @bendyarm) of these new circuits, and they look reasonable to me. I only inspected part of the constraints because there are literally thousands. Eventually we'll check all of them mechanically in the theorem prover, but I wanted to do a preliminary sanity checks on these new and slightly complicated circuits.

@acoglio acoglio marked this pull request as ready for review October 30, 2023 20:13
Comment on lines 79 to 80
let point1_times_order = order_bits_be_constants.mul(point1);
let point2_times_order = order_bits_be_constants.mul(point2);
Copy link
Member

Choose a reason for hiding this comment

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

Can we implement a version of Group::enforce_in_group as Group::is_in_group that returns a boolean?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not that I know. If we impose the enforce_in_group constraints directly on the point (x, y) to check, clearly we get unsatisfiable constraints when (x, y) is not in the group. If we postulate a new/separate point (x', y') in the group (using the enforce_in_group constraints), the trouble is how to enforce the right connection between (x, y), (x', y'), and the boolean result r. Just saying that (r = 1) <==> (x = x' & y = y') is underconstrained, because even if (x, y) is in the group, I can pick a different (x', y') also in the group (because the group has more than one point) and set r = 0 (another possible solution being x = x' and y = y' and r = 1 of course).

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'm not saying it's impossible. Maybe there's something clever, but if there is it has not occurred to any of us yet.

Copy link
Member

Choose a reason for hiding this comment

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

Looking at enforce_in_group it seems like it's possible to change the enforce_* into is_*, but I don't know what the issue is.

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 looked this morning at ProvableHQ/snarkVM-fork@fix/cast-lossy-field-to-group...feat/is-in-group (which does that) but I see an underconstraining problem similar to above. I'll DM-forward you the details.

@howardwu howardwu marked this pull request as draft December 27, 2023 05:23
@acoglio acoglio requested a review from howardwu December 27, 2023 07:37
@acoglio
Copy link
Contributor Author

acoglio commented Dec 27, 2023

@howardwu The CI failure may be due to a change in the constraint count:

thread 'transaction::serialize::tests::test_bincode' panicked at 'called `Result::unwrap()` on an `Err` value: Expected size of 12582920, found size of 573440', ledger/block/src/transaction/fee/mod.rs:256:76

But I'm not seeing explicit numbers to change in that code; presumably they are calculated, but I'm not familiar with that part of the code.

@howardwu
Copy link
Member

The CI failure is just an S3 failure. I've triggered a re-run of CI

@acoglio acoglio changed the base branch from testnet3 to mainnet December 27, 2023 20:45
@acoglio
Copy link
Contributor Author

acoglio commented Dec 27, 2023

Re-targeted to mainnet branch as requested by @d0cd.

d0cd and others added 7 commits January 2, 2024 12:11
Co-authored-by: Alessandro Coglio <[email protected]>
Signed-off-by: d0cd <[email protected]>
Co-authored-by: Alessandro Coglio <[email protected]>
Signed-off-by: d0cd <[email protected]>
[Refactor] Adds some tests and contains some documentation suggestions for the `fix/cast-lossy-field-to-group` PR
Copy link
Collaborator

@d0cd d0cd left a comment

Choose a reason for hiding this comment

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

Looks good to me! We should make a note to investigate improvements to the number of constraint counts required for is_in_group.

@acoglio acoglio marked this pull request as ready for review January 6, 2024 18:33
@@ -63,6 +63,65 @@ impl<E: Environment> Field<E> {
}
}

impl<E: Environment> Field<E> {
/// Returns both square roots of `self` and a `Boolean` flag, which is set iff `self` is not a square.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Suggested change
/// Returns both square roots of `self` and a `Boolean` flag, which is set iff `self` is not a square.
/// Returns both square roots of `self` and a `Boolean` flag, which is set iff `self` is not a square.
/// The roots are returned in the order specified below by this function,
/// but the generated constraints do not impose an order
/// (this is where the `nondeterministic` part of this function name refers to).

@howardwu howardwu merged commit 34acf76 into mainnet Jan 9, 2024
78 checks passed
@howardwu howardwu deleted the fix/cast-lossy-field-to-group branch January 9, 2024 03:58
@howardwu howardwu changed the title Fix circuit for cast lossy field to group. [Fix] Fix circuit for cast lossy field to group. Feb 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants