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 gadgets to API surface, UInt32, UInt64, implement rot32 #1259

Merged
merged 62 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
fb543ed
feat(int.ts): add support for bitwise XOR, NOT, rotate, leftShift, ri…
Trivo25 Nov 14, 2023
840fc7a
fix(int.ts): update parameter name in rotate method from 'direction' …
Trivo25 Nov 14, 2023
f6f9177
fix(int.ts): fix instantiation of UInt64 in example code by using the…
Trivo25 Nov 14, 2023
5020ce3
simplify doc comments, avoid confusion
Trivo25 Nov 21, 2023
63fa85f
same for uint32
Trivo25 Nov 21, 2023
14f2051
Merge branch 'main' into feature/gadgets-uint
Trivo25 Nov 21, 2023
3932338
bump bindings
Trivo25 Nov 21, 2023
8c524b6
add divMod32, addMod32
Trivo25 Nov 21, 2023
7d8ff8e
rotate32, rename rotate to rotate64
Trivo25 Nov 21, 2023
27d6f8a
add rotate32 tests
Trivo25 Nov 21, 2023
0d5e09b
epose rot32
Trivo25 Nov 21, 2023
81c90c4
rangeCheck32
Trivo25 Nov 21, 2023
aafd4b8
add rotate32 to UInt32
Trivo25 Nov 21, 2023
6419176
fix tests
Trivo25 Nov 21, 2023
e8cac1a
fix vk regression test
Trivo25 Nov 21, 2023
4685946
add constant check
Trivo25 Nov 22, 2023
4013f14
re-add range checks
Trivo25 Nov 22, 2023
bc9a20e
add doc comments
Trivo25 Nov 22, 2023
14a7717
rename r and q to remainder and quotient
Trivo25 Nov 22, 2023
51d0bfd
undo chain tests because not needed
Trivo25 Nov 22, 2023
ce34be8
Undo comment
Trivo25 Nov 22, 2023
4d78fc0
bump bindings
Trivo25 Nov 22, 2023
d370e29
fix return type opf witness
Trivo25 Nov 22, 2023
08069bb
limit input to 64bit, add tests
Trivo25 Nov 22, 2023
954607e
adjust error message
Trivo25 Nov 22, 2023
0878630
Merge branch 'main' into feature/gadgets-uint
Trivo25 Nov 22, 2023
e25d896
add gadgets to regression tests
Trivo25 Nov 22, 2023
62c1635
simplify tests
Trivo25 Nov 28, 2023
93be2b0
refactor rangeCheckHelper
Trivo25 Nov 28, 2023
12f388d
fix import
Trivo25 Nov 28, 2023
982d3b0
Merge branch 'main' into feature/gadgets-uint
Trivo25 Nov 28, 2023
2b091e4
changelog
Trivo25 Nov 28, 2023
f6e43a2
Update src/lib/gadgets/gadgets.ts
Trivo25 Nov 29, 2023
89d806b
Update src/lib/gadgets/gadgets.ts
Trivo25 Nov 29, 2023
72d38b4
Update src/lib/gadgets/gadgets.ts
Trivo25 Nov 29, 2023
84f0823
address feedback
Trivo25 Nov 29, 2023
d790149
Merge branch 'feature/gadgets-uint' of https://github.com/o1-labs/o1j…
Trivo25 Nov 29, 2023
cf16482
fix changelog
Trivo25 Nov 29, 2023
2042e02
add rangeCheckN and isInRangeN
Trivo25 Nov 29, 2023
b97c717
add correctly typed constructor for UInt32 and UInt64
Trivo25 Nov 29, 2023
973a366
fix vk tests
Trivo25 Nov 29, 2023
1165c5f
remove constant check for now
Trivo25 Nov 29, 2023
8cefdbb
add left shift(temp)
Trivo25 Nov 29, 2023
6da47bd
remove debug code
Trivo25 Nov 29, 2023
2f18e5f
fix tests
Trivo25 Nov 29, 2023
1b3ce9b
handle constant case
Trivo25 Nov 29, 2023
ff91e1d
fix tests
Trivo25 Nov 29, 2023
a0f1e5e
undo spacing in example
Trivo25 Nov 29, 2023
de71797
fix compile issue
Trivo25 Nov 29, 2023
fb2aabd
clean up
Trivo25 Nov 29, 2023
b3d74df
doc comment fix
Trivo25 Nov 29, 2023
12942ed
Merge branch 'main' into feature/gadgets-uint
Trivo25 Dec 15, 2023
cfa4883
return UInt instead of field
Trivo25 Dec 15, 2023
9702793
fix compilation, bump bindings
Trivo25 Dec 15, 2023
7b5e8fa
fix doc comment
Trivo25 Dec 15, 2023
673c822
remove unused imports
Trivo25 Dec 15, 2023
108513c
resolve dependency issue
Trivo25 Dec 19, 2023
fc70678
resolve dependency issue, yet again
Trivo25 Dec 19, 2023
a8ff0aa
return UInt32 instead of Field
Trivo25 Dec 19, 2023
d4f27be
changelog
Trivo25 Dec 19, 2023
c1e6087
Merge branch 'main' into feature/gadgets-uint
Trivo25 Dec 19, 2023
e2785ab
fix correct bindings
Trivo25 Dec 19, 2023
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
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,31 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased](https://github.com/o1-labs/o1js/compare/7acf19d0d...HEAD)

### Breaking changes

- Rename `Gadgets.rotate()` to `Gadgets.rotate64()` to better reflect the amount of bits the gadget operates on. https://github.com/o1-labs/o1js/pull/1259
- Rename `Gadgets.{leftShift(), rightShift()}` to `Gadgets.{leftShift64(), rightShift64()}` to better reflect the amount of bits the gadget operates on. https://github.com/o1-labs/o1js/pull/1259

### Added

- Non-native elliptic curve operations exposed through `createForeignCurve()` class factory https://github.com/o1-labs/o1js/pull/1007
- **ECDSA signature verification** exposed through `createEcdsa()` class factory https://github.com/o1-labs/o1js/pull/1240 https://github.com/o1-labs/o1js/pull/1007

- For an example, see `./src/examples/crypto/ecdsa`

- `Gadgets.rotate32()` for rotation over 32 bit values https://github.com/o1-labs/o1js/pull/1259
- `Gadgets.leftShift32()` for left shift over 32 bit values https://github.com/o1-labs/o1js/pull/1259
- `Gadgets.divMod32()` division modulo 2^32 that returns the remainder and quotient of the operation https://github.com/o1-labs/o1js/pull/1259
- `Gadgets.rangeCheck32()` range check for 32 bit values https://github.com/o1-labs/o1js/pull/1259
- `Gadgets.addMod32()` addition modulo 2^32 https://github.com/o1-labs/o1js/pull/1259
- Expose new bitwise gadgets on `UInt32` and `UInt64` https://github.com/o1-labs/o1js/pull/1259
- bitwise XOR via `{UInt32, UInt64}.xor()`
- bitwise NOT via `{UInt32, UInt64}.not()`
- bitwise ROTATE via `{UInt32, UInt64}.rotate()`
- bitwise LEFTSHIFT via `{UInt32, UInt64}.leftShift()`
- bitwise RIGHTSHIFT via `{UInt32, UInt64}.rightShift()`
- bitwise AND via `{UInt32, UInt64}.and()`

### Fixed

- Fix stack overflows when calling provable methods with large inputs https://github.com/o1-labs/o1js/pull/1334
Expand Down
8 changes: 4 additions & 4 deletions src/examples/zkprogram/gadgets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { Field, Provable, Gadgets, ZkProgram } from 'o1js';
let cs = Provable.constraintSystem(() => {
let f = Provable.witness(Field, () => Field(12));

let res1 = Gadgets.rotate(f, 2, 'left');
let res2 = Gadgets.rotate(f, 2, 'right');
let res1 = Gadgets.rotate64(f, 2, 'left');
let res2 = Gadgets.rotate64(f, 2, 'right');

res1.assertEquals(Field(48));
res2.assertEquals(Field(3));
Expand All @@ -21,8 +21,8 @@ const BitwiseProver = ZkProgram({
privateInputs: [],
method: () => {
let a = Provable.witness(Field, () => Field(48));
let actualLeft = Gadgets.rotate(a, 2, 'left');
let actualRight = Gadgets.rotate(a, 2, 'right');
let actualLeft = Gadgets.rotate64(a, 2, 'left');
let actualRight = Gadgets.rotate64(a, 2, 'right');

let expectedLeft = Field(192);
actualLeft.assertEquals(expectedLeft);
Expand Down
48 changes: 48 additions & 0 deletions src/lib/gadgets/arithmetic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { provableTuple } from '../circuit_value.js';
import { Field } from '../core.js';
Trivo25 marked this conversation as resolved.
Show resolved Hide resolved
import { assert } from '../errors.js';
import { Provable } from '../provable.js';
import { rangeCheck32 } from './range-check.js';

export { divMod32, addMod32 };

function divMod32(n: Field) {
if (n.isConstant()) {
assert(
n.toBigInt() < 1n << 64n,
`n needs to fit into 64 bit, but got ${n.toBigInt()}`
);
Trivo25 marked this conversation as resolved.
Show resolved Hide resolved

let nBigInt = n.toBigInt();
let q = nBigInt >> 32n;
let r = nBigInt - (q << 32n);
return {
remainder: new Field(r),
quotient: new Field(q),
};
}

let [quotient, remainder] = Provable.witness(
provableTuple([Field, Field]),
() => {
let nBigInt = n.toBigInt();
let q = nBigInt >> 32n;
let r = nBigInt - (q << 32n);
return [new Field(q), new Field(r)];
}
);

rangeCheck32(quotient);
rangeCheck32(remainder);

n.assertEquals(quotient.mul(1n << 32n).add(remainder));

return {
remainder,
quotient,
};
}

function addMod32(x: Field, y: Field) {
return divMod32(x.add(y)).remainder;
}
59 changes: 59 additions & 0 deletions src/lib/gadgets/arithmetic.unit-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { ZkProgram } from '../proof_system.js';
import {
equivalentProvable as equivalent,
equivalentAsync,
field,
record,
} from '../testing/equivalent.js';
import { Field } from '../core.js';
import { Gadgets } from './gadgets.js';
import { provable } from '../circuit_value.js';
import { assert } from './common.js';

let Arithmetic = ZkProgram({
name: 'arithmetic',
publicOutput: provable({
remainder: Field,
quotient: Field,
}),
methods: {
divMod32: {
privateInputs: [Field],
method(a: Field) {
return Gadgets.divMod32(a);
},
},
},
});

await Arithmetic.compile();

const divMod32Helper = (x: bigint) => {
let quotient = x >> 32n;
let remainder = x - (quotient << 32n);
return { remainder, quotient };
};
const divMod32Output = record({ remainder: field, quotient: field });
Trivo25 marked this conversation as resolved.
Show resolved Hide resolved

equivalent({
from: [field],
to: divMod32Output,
})(
(x) => {
assert(x < 1n << 64n, `x needs to fit in 64bit, but got ${x}`);
return divMod32Helper(x);
},
(x) => {
return Gadgets.divMod32(x);
}
);

await equivalentAsync({ from: [field], to: divMod32Output }, { runs: 3 })(
(x) => {
assert(x < 1n << 64n, `x needs to fit in 64bit, but got ${x}`);
return divMod32Helper(x);
},
async (x) => {
return (await Arithmetic.divMod32(x)).publicOutput;
}
);
63 changes: 52 additions & 11 deletions src/lib/gadgets/bitwise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,19 @@ import {
exists,
bitSlice,
} from './common.js';
import { rangeCheck64 } from './range-check.js';

export { xor, not, rotate, and, rightShift, leftShift };
import { rangeCheck32, rangeCheck64 } from './range-check.js';
import { divMod32 } from './arithmetic.js';

export {
xor,
not,
rotate64,
rotate32,
and,
rightShift64,
leftShift64,
leftShift32,
};

function not(a: Field, length: number, checked: boolean = false) {
// check that input length is positive
Expand Down Expand Up @@ -187,7 +197,7 @@ function and(a: Field, b: Field, length: number) {
return outputAnd;
}

function rotate(
function rotate64(
field: Field,
bits: number,
direction: 'left' | 'right' = 'left'
Expand All @@ -200,16 +210,42 @@ function rotate(

if (field.isConstant()) {
assert(
field.toBigInt() < 2n ** BigInt(MAX_BITS),
field.toBigInt() < 1n << BigInt(MAX_BITS),
`rotation: expected field to be at most 64 bits, got ${field.toBigInt()}`
);
return new Field(Fp.rot(field.toBigInt(), BigInt(bits), direction));
}
const [rotated] = rot(field, bits, direction);
const [rotated] = rot64(field, bits, direction);
return rotated;
}

function rotate32(
Trivo25 marked this conversation as resolved.
Show resolved Hide resolved
field: Field,
bits: number,
direction: 'left' | 'right' = 'left'
) {
assert(bits <= 32 && bits > 0, 'bits must be between 0 and 32');

if (field.isConstant()) {
assert(
field.toBigInt() < 1n << 32n,
`rotation: expected field to be at most 32 bits, got ${field.toBigInt()}`
);
return new Field(Fp.rot(field.toBigInt(), BigInt(bits), direction, 32n));
}

let { quotient: excess, remainder: shifted } = divMod32(
field.mul(1n << BigInt(direction === 'left' ? bits : 32 - bits))
);

let rotated = shifted.add(excess);

rangeCheck32(rotated);

return rotated;
}

function rot(
function rot64(
field: Field,
bits: number,
direction: 'left' | 'right' = 'left'
Expand Down Expand Up @@ -281,7 +317,7 @@ function rot(
return [rotated, excess, shifted];
}

function rightShift(field: Field, bits: number) {
function rightShift64(field: Field, bits: number) {
assert(
bits >= 0 && bits <= MAX_BITS,
`rightShift: expected bits to be between 0 and 64, got ${bits}`
Expand All @@ -294,11 +330,11 @@ function rightShift(field: Field, bits: number) {
);
return new Field(Fp.rightShift(field.toBigInt(), bits));
}
const [, excess] = rot(field, bits, 'right');
const [, excess] = rot64(field, bits, 'right');
return excess;
}

function leftShift(field: Field, bits: number) {
function leftShift64(field: Field, bits: number) {
assert(
bits >= 0 && bits <= MAX_BITS,
`rightShift: expected bits to be between 0 and 64, got ${bits}`
Expand All @@ -311,6 +347,11 @@ function leftShift(field: Field, bits: number) {
);
return new Field(Fp.leftShift(field.toBigInt(), bits));
}
const [, , shifted] = rot(field, bits, 'left');
const [, , shifted] = rot64(field, bits, 'left');
return shifted;
}

function leftShift32(field: Field, bits: number) {
let { remainder: shifted } = divMod32(field.mul(1n << BigInt(bits)));
return shifted;
}
Loading