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 bounds checks to GPC #1823

Merged
merged 6 commits into from
Jul 6, 2024
Merged

Add bounds checks to GPC #1823

merged 6 commits into from
Jul 6, 2024

Conversation

ax0
Copy link
Collaborator

@ax0 ax0 commented Jul 2, 2024

Resolves https://linear.app/0xparc-pcd/issue/0XP-951/gpc-lowerboundupperbound-proofs

This PR adds bounds checks to the GPC config. Summary of changes:

  • Addition of minValue and maxValue fields to the proof entry config
  • Additional checks and compilation steps in GPC package
  • Unit tests
  • POD PCD filtering for prescribed bounds in GPC PCD package

@ax0 ax0 self-assigned this Jul 2, 2024
@ax0 ax0 requested a review from artwyman July 2, 2024 04:16
Copy link
Member

@artwyman artwyman left a comment

Choose a reason for hiding this comment

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

As usual, I reviewed the functional code but not the tests yet, which I'll save for a later round.
I made some structural and interface-level comments which I'd like to hear your thoughts on, since they could motivate some changes to the spec or config types.

packages/lib/gpc/src/gpcTypes.ts Outdated Show resolved Hide resolved
* 64-bit integer value and will be revealed by virtue of its inclusion in the
* proof configuration.
*/
minValue?: bigint;
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure, but I think these should maybe be PODValue rather than bigint.

Advantage of bare bigint value is terseness. It also directly represents what happens in the circuit.

Advantage of PODValue is that supported range is implicitly specified by the type, and it'll work in future for differently-encoded numeric values, such as a date.

PODValue also gives a slightly simpler path toward JSON serialization (assuming PODValue is a type for which serialization will be solved). And if an eventual JSONPODValue type includes number as an option for encoding small ints, then we get back to terseness.

I'm not sure if this is worth dealing with now. It can wait to be dealt with along with one of the features which make it more valuable (date type, PODValue serialization).

packages/lib/gpc/src/gpcUtil.ts Outdated Show resolved Hide resolved
packages/lib/gpc/src/gpcTypes.ts Show resolved Hide resolved
packages/lib/gpc/src/gpcChecks.ts Outdated Show resolved Hide resolved
packages/lib/gpc/src/gpcChecks.ts Outdated Show resolved Hide resolved
packages/lib/gpc/src/gpcCompile.ts Outdated Show resolved Hide resolved
packages/lib/gpc/src/gpcCompile.ts Outdated Show resolved Hide resolved
packages/lib/gpc/src/gpcCompile.ts Outdated Show resolved Hide resolved
@ax0 ax0 requested a review from artwyman July 5, 2024 00:21
@ax0 ax0 force-pushed the bounds-in-gpc branch from 943e908 to 0716f3a Compare July 5, 2024 06:21
Copy link
Member

@artwyman artwyman left a comment

Choose a reason for hiding this comment

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

My remaining comments are about style rather than functionality, so I'm accepting now and will review any responses post-merge.

@@ -130,6 +130,14 @@ export type GPCProofEntryConfigCommon = {
* and constraints should be enabled for that entry.
*/
export type GPCProofEntryConfig = GPCProofEntryConfigCommon & {
/**
Copy link
Member

Choose a reason for hiding this comment

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

In future, we could consider shortcuts for this if we think it's too verbose. E.g. inRange: "int" might be a shortcut for checking a value against POD_INT_MIN and POD_INT_MAX. Or inRange: {min: 100, max: "int"} could be a shortcut for a "one-sided" bounds-check, given that we can't really support truly one-sided bounds.

This is most likely to come up when we add further modules which require a bounds-check as a pre-requisite (e.g. ordered comparison, summing values). I'm not sure yet the best way to represent that requirement, and whether it should be implicit (if you compare two fields, the bounds checks happen silently) or explicit (you're required to specify the bounds check on the fields you compare).

packages/lib/gpc/src/gpcUtil.ts Outdated Show resolved Hide resolved
packages/lib/gpc/src/gpcUtil.ts Show resolved Hide resolved
packages/lib/gpc/src/gpcChecks.ts Outdated Show resolved Hide resolved
packages/lib/gpc/src/gpcCompile.ts Outdated Show resolved Hide resolved
packages/lib/gpc/src/gpcCompile.ts Outdated Show resolved Hide resolved
packages/lib/gpc/src/gpcCompile.ts Outdated Show resolved Hide resolved
packages/lib/gpc/src/gpcCompile.ts Outdated Show resolved Hide resolved
packages/lib/gpc/src/gpcCompile.ts Outdated Show resolved Hide resolved
@ax0 ax0 added this pull request to the merge queue Jul 6, 2024
Merged via the queue into proofcarryingdata:main with commit e04c459 Jul 6, 2024
1 check passed
const hasBoundsCheck = entryConfig.inRange !== undefined;

if (hasBoundsCheck) {
const inRange = entryConfig.inRange as BoundsConfig;
Copy link
Member

Choose a reason for hiding this comment

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

I think this could be satisfies rather than as.
As I understand it, the former will compile only if the input really does match the fields for right type, while the latter will "take your word for it" and not trigger a compile error if you did something wrong.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Agreed, but weirdly enough the compiler still thinks it could be undefined so that satisfies does not work, which is why I introduced this variable to do the type coercion for the following lines. It is a different story with if(hasBoundsCheck) replaced with if(entryConfig.inRange !== undefined).

Copy link
Member

Choose a reason for hiding this comment

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

Oh, yeah I've seen this particular bit of dumb typechecking before.
I think if you saved the bounds in a variable and then checked that variable directly against undefined in your if statement the compiler would see that it's defined, but then you return statement would get more complicated.
I wish there were a more limited form for a compile-time assertion that something is defined, without fully overriding its type.

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