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

Move low-level operations to JS #967

Merged
merged 24 commits into from
Jun 20, 2023
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased](https://github.com/o1-labs/snarkyjs/compare/3fbd9678e...HEAD)

> no unreleased changes yet
### Breaking changes

- `Group` operations now generate a different set of constraints. This breaks deployed contracts, because the circuit changed. https://github.com/o1-labs/snarkyjs/pull/967

## [0.11.0](https://github.com/o1-labs/snarkyjs/compare/a632313a...3fbd9678e)

Expand Down
2 changes: 1 addition & 1 deletion src/bindings
176 changes: 156 additions & 20 deletions src/lib/group.test.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,46 @@
import {
shutdown,
isReady,
Field,
Bool,
Circuit,
Group,
Scalar,
Provable,
} from 'snarkyjs';
import { Bool, Group, Scalar, Provable } from 'snarkyjs';

describe('group', () => {
let g = Group({
x: -1,
y: 2,
});
beforeAll(async () => {
await isReady;
});

afterAll(async () => {
setTimeout(async () => {
await shutdown();
}, 0);
});

describe('Inside circuit', () => {
describe('group membership', () => {
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 use the quickcheck generator Gregor made earlier to check some of the algebraic properties for edge cases?

Copy link
Member Author

Choose a reason for hiding this comment

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

I was thinking about that as well, however, I think there is no easy way to sample valid Group elements other than brute forcing (or is there?) that's why the existing tests mostly work with the generator and (-1, 2)

Copy link
Member

Choose a reason for hiding this comment

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

I used to sample public keys (= group elements) by picking a random private key (= scalar) and scaling the group generator with it.
That's not super efficient but good enough.

However, now we have hashToCurve which should be faster, especially if you skip the hash and only do random field element + mapToCurve

Copy link
Member Author

@Trivo25 Trivo25 Jun 14, 2023

Choose a reason for hiding this comment

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

Oh right, I didn't consider that at all 😵‍💫 I'll add some more tests, just to be sure!

it('valid element does not throw', () => {
expect(() => {
Provable.runAndCheck(() => {
Provable.witness(Group, () => g);
});
}).not.toThrow();
});

it('valid element does not throw', () => {
expect(() => {
Provable.runAndCheck(() => {
Provable.witness(Group, () => Group.generator);
});
}).not.toThrow();
});

it('Group.zero element does not throw', () => {
expect(() => {
Provable.runAndCheck(() => {
Provable.witness(Group, () => Group.zero);
});
}).not.toThrow();
});

it('invalid group element throws', () => {
expect(() => {
Provable.runAndCheck(() => {
Provable.witness(Group, () => Group({ x: 2, y: 2 }));
});
}).toThrow();
});
});

describe('add', () => {
it('g+g does not throw', () => {
expect(() => {
Expand All @@ -35,6 +51,46 @@ describe('group', () => {
});
}).not.toThrow();
});

it('g+zero = g', () => {
expect(() => {
Provable.runAndCheck(() => {
const x = Provable.witness(Group, () => g);
const zero = Provable.witness(Group, () => Group.zero);
x.add(zero).assertEquals(x);
});
}).not.toThrow();
});

it('zero+g = g', () => {
expect(() => {
Provable.runAndCheck(() => {
const x = Provable.witness(Group, () => g);
const zero = Provable.witness(Group, () => Group.zero);
zero.add(x).assertEquals(x);
});
}).not.toThrow();
});

it('g+(-g) = zero', () => {
expect(() => {
Provable.runAndCheck(() => {
const x = Provable.witness(Group, () => g);
const zero = Provable.witness(Group, () => Group.zero);
x.add(x.neg()).assertEquals(zero);
});
Trivo25 marked this conversation as resolved.
Show resolved Hide resolved
}).not.toThrow();
});

it('(-g)+g = zero', () => {
expect(() => {
Provable.runAndCheck(() => {
const x = Provable.witness(Group, () => g);
const zero = Provable.witness(Group, () => Group.zero);
x.neg().add(x).assertEquals(zero);
});
}).not.toThrow();
});
});
Trivo25 marked this conversation as resolved.
Show resolved Hide resolved

describe('sub', () => {
Expand All @@ -49,6 +105,26 @@ describe('group', () => {
});
}).not.toThrow();
});

it('g-zero = g', () => {
expect(() => {
Provable.runAndCheck(() => {
const x = Provable.witness(Group, () => g);
const zero = Provable.witness(Group, () => Group.zero);
x.sub(zero).assertEquals(x);
});
}).not.toThrow();
});

it('zero - g = -g', () => {
expect(() => {
Provable.runAndCheck(() => {
const x = Provable.witness(Group, () => g);
const zero = Provable.witness(Group, () => Group.zero);
zero.sub(x).assertEquals(x.neg());
});
}).not.toThrow();
});
});

describe('neg', () => {
Expand All @@ -60,6 +136,15 @@ describe('group', () => {
});
}).not.toThrow();
});

it('neg(zero) = zero', () => {
expect(() => {
Provable.runAndCheck(() => {
const zero = Provable.witness(Group, () => Group.zero);
zero.neg().assertEquals(zero);
});
}).not.toThrow();
});
});

describe('scale', () => {
Expand Down Expand Up @@ -161,6 +246,13 @@ describe('group', () => {
g.neg();
}).not.toThrow();
});

it('zero.neg = zero', () => {
expect(() => {
const zero = Group.zero;
zero.neg().assertEquals(zero);
}).not.toThrow();
});
});

describe('add', () => {
Expand All @@ -169,6 +261,34 @@ describe('group', () => {
g.add(g);
}).not.toThrow();
});

it('g + zero = g', () => {
expect(() => {
const zero = Group.zero;
g.add(zero).assertEquals(g);
}).not.toThrow();
});

it('zero + g = g', () => {
expect(() => {
const zero = Group.zero;
zero.add(g).assertEquals(g);
}).not.toThrow();
});

it('g + (-g) = zero', () => {
expect(() => {
const zero = Group.zero;
g.add(g.neg()).assertEquals(zero);
}).not.toThrow();
});

it('(-g) + g = zero', () => {
expect(() => {
const zero = Group.zero;
g.neg().add(g).assertEquals(zero);
}).not.toThrow();
});
});

describe('sub', () => {
Expand All @@ -177,6 +297,20 @@ describe('group', () => {
Group.generator.sub(g);
}).not.toThrow();
});

it('g - zero = g', () => {
expect(() => {
const zero = Group.zero;
g.sub(zero).assertEquals(g);
}).not.toThrow();
});

it('zero - g = -g', () => {
expect(() => {
const zero = Group.zero;
zero.sub(g).assertEquals(g.neg());
}).not.toThrow();
});
});

describe('scale', () => {
Expand Down Expand Up @@ -231,18 +365,20 @@ describe('group', () => {
y.assertEquals(z);
});
});

it('sub', () => {
let y = Provable.witness(Group, () => g).sub(
Provable.witness(Group, () => Group.generator)
);
let z = g.sub(Group.generator);
y.assertEquals(z);
});

it('sub', () => {
let y = Provable.witness(Group, () => g).assertEquals(
Provable.witness(Group, () => g)
);
let z = g.assertEquals(g);
g.assertEquals(g);
});
});
});
Loading
Loading