-
Notifications
You must be signed in to change notification settings - Fork 146
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #737 from o1-labs/gadgets
docs: add bitwise operation gadgets to o1js
- Loading branch information
Showing
3 changed files
with
374 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,344 @@ | ||
--- | ||
title: Bitwise Operations | ||
hide_title: true | ||
description: Bitwise operations in o1js are implemented as gadgets. | ||
keywords: | ||
- gadgets | ||
- bitwise operations | ||
- o1js | ||
--- | ||
|
||
:::info | ||
|
||
Please note that zkApp programmability is not yet available on Mina Mainnet, but zkApps can now be deployed to Berkeley Testnet. | ||
|
||
::: | ||
|
||
# Bitwise Operations | ||
|
||
Bitwise operations manipulate individual bits within a binary representation of a number. They can at times resemble boolean operations but apply to a sequence of bits instead of booleans. Bitwise operations are generally available in most programming languages, including TypeScript. o1js provides versions of them that operate on `Field` elements and result in the necessary circuit constraints to generate a zero knowledge proof of the computation. This is especially useful when implementing hashing algorithms such as SHA256. | ||
|
||
In o1js, bitwise operations and their attendant helper functions are implemented as [gadgets](/zkapps/o1js/gadgets). | ||
|
||
Bitwise operations: | ||
- [and()](#and) | ||
- [not()](#not) | ||
- [xor()](#xor) | ||
- [leftShift32()](#leftshift32) | ||
- [leftShift64()](#leftshift64) | ||
- [rightShift64()](#rightshift64) | ||
- [rotate32()](#rotate32) | ||
- [rotate64()](#rotate64) | ||
|
||
Helper functions: | ||
- [addMod32()](#addmod32) | ||
- [divMod32()](#divmod32) | ||
- [rangeCheck32()](#rangecheck32) | ||
- [rangeCheck64()](#rangecheck64) | ||
- [multiRangeCheck()](#multirangecheck) | ||
- [compactMultiRangeCheck()](#compactmultirangecheck) | ||
|
||
## and() | ||
|
||
```ts | ||
and(a: Field, b: Field, length: number) => Field | ||
``` | ||
|
||
The bitwise `and()` gadget is a provable equivalent to the [bitwise AND (&)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_AND) operator in JavaScript. It receives two `Field` elements and compares the corresponding pairs of bits from the binary representation of each. The comparison returns 1 only if both bits are 1 and returns 0 if either bit is not 1. This results in a new binary number which is returned as a `Field` element. | ||
|
||
For details about the implementation, see [AND](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#and) in the Mina book. | ||
|
||
The `length` parameter: | ||
|
||
- Specifies how many bits to compare. | ||
- Adds more constraints for larger numbers. | ||
|
||
Example: | ||
|
||
```ts | ||
let a = Field(3); // ... 000011 | ||
let b = Field(5); // ... 000101 | ||
let c = Gadgets.and(a, b, 2); // ... 000001 | ||
c.assertEquals(1); | ||
``` | ||
|
||
## not() | ||
|
||
```ts | ||
not(a: Field, length: number, checked: boolean) => Field | ||
``` | ||
|
||
The bitwise `not()` gadget is a provable equivalent to the [Bitwise NOT (~)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_NOT) operator in JavaScript. It receives a `Field` element and negates each bit of its binary representation, turning all the 1s into 0s and all the 0s into 1s. It essentially flips all the bits in a `Field` element. This results in a new binary number which is returned as a `Field` element. | ||
|
||
The implementation varies depending on whether the input length is checked. Not checking the input length is more efficient. The input is substracted from an all-ones bitmask (where all the bits in a binary sequence are set to 1). The tradeoff is that you need to know the input length upfront. This is safe when the input `Field` is the result of some other proven operation with a known output length. When the input length is checked, however, the [xor()](#xor) gadget is reused. An all-ones bitmask of equal length to the input `Field` is supplied as second argument. This results in the same operation with proven input length and more constraints. | ||
|
||
The input `Field` must be 254 bits or less. | ||
|
||
The `length` parameter: | ||
|
||
- Specifies how many bits to negate. | ||
- Adds more constraints for larger numbers. | ||
|
||
The `checked` parameter: | ||
|
||
- Specifies whether to check the length of the input. | ||
- Defaults to `false`. | ||
|
||
For details about the implementation, see [NOT](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#not) in the Mina book. | ||
|
||
Example: | ||
|
||
```ts | ||
let a = Field(0b0101); | ||
let b = Gadgets.not(a,4,true); | ||
|
||
b.assertEquals(0b1010); | ||
``` | ||
|
||
## xor() | ||
|
||
```ts | ||
xor(a: Field, b: Field, length: number) => Field | ||
``` | ||
|
||
The `xor()` gadget is a provable equivalent to the [Bitwise XOR (^)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_XOR) operator in JavaScript. It receives two `Field` elements and compares the corresponding pairs of bits from the binary representation of each. The comparison returns 1 if the bits differ and 0 if they are the same. This results in a new binary number which is returned as a `Field` element. | ||
|
||
The `length` parameter: | ||
|
||
- Specifies how many bits to compare. | ||
- Adds more constraints for larger numbers. | ||
|
||
For details about the implementation, see [XOR](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#xor-1) in the Mina book. | ||
|
||
Example: | ||
|
||
```ts | ||
let a = Field(0b0101); | ||
let b = Field(0b0011); | ||
|
||
let c = Gadgets.xor(a, b, 4); // xor-ing 4 bits | ||
c.assertEquals(0b0110); | ||
``` | ||
|
||
## leftShift32() | ||
|
||
```ts | ||
leftShift32(field: Field, bits: number) => Field | ||
``` | ||
|
||
The `leftShift32()` gadget is a provable equivalent to the [Left shift (<<)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Left_shift) operator in JavaScript. It moves the bits of a binary number to the left by the specified number of `bits`. Any bits that fall off the left side are discarded. 0s are padded in from the right. It returns a new `Field` element that is range checked to 32 bits. | ||
|
||
The input `Field` must not exceed 32 bits in size. You can use [rangeCheck32](#rangecheck32) to ensure this. | ||
|
||
Example: | ||
|
||
```ts | ||
const x = Provable.witness(Field, () => Field(0b001100)); // 12 in binary | ||
const y = Gadgets.leftShift32(x, 2); // left shift by 2 bits | ||
y.assertEquals(0b110000); // 48 in binary | ||
``` | ||
|
||
## leftShift64() | ||
|
||
```ts | ||
leftShift64(field: Field, bits: number) => Field | ||
``` | ||
|
||
The `leftShift64()` gadget is a provable equivalent to the [Left shift (<<)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Left_shift) operator in JavaScript. It moves the bits of a binary number to the left by the specified number of `bits`. Any bits that fall off the left side are discarded. 0s are padded in from the right. It returns a new `Field` element that is range checked to 64 bits. | ||
|
||
The input `Field` must not exceed 64 bits in size. You can use [rangeCheck64](#rangecheck64) to ensure this. | ||
|
||
Example: | ||
|
||
```ts | ||
const x = Provable.witness(Field, () => Field(0b001100)); // 12 in binary | ||
const y = Gadgets.leftShift64(x, 2); // left shift by 2 bits | ||
y.assertEquals(0b110000); // 48 in binary | ||
|
||
const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); | ||
Gadgets.leftShift64(xLarge, 32); // throws an error since input exceeds 64 bits | ||
``` | ||
|
||
## rightShift64() | ||
|
||
```ts | ||
rightShift64(field: Field, bits: number) => Field | ||
``` | ||
|
||
The `rightShift64()` gadget is a provable equivalent to the [Right shift (>>)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Right_shift) operator in JavaScript. It moves the bits of a binary number to the right by the specified number of `bits`. Any bits that fall off the right side are discarded. 0s are padded in from the left. It returns a new `Field` element. | ||
|
||
The input `Field` must not exceed 64 bits in size. You can use [rangeCheck64](#rangecheck64) to ensure this. | ||
|
||
Example: | ||
```ts | ||
const x = Provable.witness(Field, () => Field(0b001100)); // 12 in binary | ||
const y = Gadgets.rightShift64(x, 2); // right shift by 2 bits | ||
y.assertEquals(0b000011); // 3 in binary | ||
|
||
const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); | ||
Gadgets.rightShift64(xLarge, 32); // throws an error since input exceeds 64 bits | ||
``` | ||
|
||
## rotate32() | ||
|
||
```ts | ||
rotate32(field: Field, bits: number, direction: 'left' | 'right' = 'left') { | ||
return rotate32(field, bits, direction); | ||
}, | ||
``` | ||
|
||
The `rotate32()` gadget performs provable bit rotation on 32-bit numbers. It is similar to left- and right shift, except the bits that fall off the end wrap around to reappear on the opposite side instead of being discarded. It accepts a `Field` element, the number of `bits` to rotate, and a `direction` of left or right. The default direction is left. | ||
|
||
The input `Field` must not exceed 32 bits in size. You can use [rangeCheck32](#rangecheck32) to ensure this. | ||
|
||
For implementation details, see [ROTATION](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#rotation) in the Mina book. | ||
|
||
Example: | ||
|
||
```ts | ||
const x = Provable.witness(Field, () => Field(0b001100)); | ||
const y = Gadgets.rotate32(x, 2, 'left'); // left rotation by 2 bits | ||
const z = Gadgets.rotate32(x, 2, 'right'); // right rotation by 2 bits | ||
y.assertEquals(0b110000); | ||
z.assertEquals(0b000011); | ||
|
||
const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); | ||
Gadgets.rotate32(xLarge, 32, "left"); // throws an error since input exceeds 32 bits | ||
``` | ||
|
||
## rotate64() | ||
|
||
```ts | ||
rotate64(field: Field, bits: number, direction: 'left' | 'right' = 'left') { | ||
return rotate64(field, bits, direction); | ||
}, | ||
``` | ||
|
||
The `rotate64()` gadget performs provable bit rotation on 32-bit numbers. It is similar to left- and right shift, except the bits that fall off the end wrap around to reappear on the opposite side instead of being discarded. It accepts a `Field` element, the number of `bits` to rotate, and a `direction` of left or right. The default direction is left. | ||
|
||
The input `Field` must not exceed 64 bits in size. You can use [rangeCheck64](#rangecheck64) to ensure this. | ||
|
||
For implementation details, see [ROTATION](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#rotation) in the Mina book. | ||
|
||
|
||
Example: | ||
|
||
```ts | ||
const x = Provable.witness(Field, () => Field(0b001100)); | ||
const y = Gadgets.rotate64(x, 2, 'left'); // left rotation by 2 bits | ||
const z = Gadgets.rotate64(x, 2, 'right'); // right rotation by 2 bits | ||
y.assertEquals(0b110000); | ||
z.assertEquals(0b000011); | ||
|
||
const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); | ||
Gadgets.rotate64(xLarge, 32, "left"); // throws an error since input exceeds 64 bits | ||
``` | ||
|
||
## addMod32() | ||
|
||
```ts | ||
addMod32(a: Field, b: Field) => Field | ||
``` | ||
|
||
The `addMod32()` helper performs addition that overflows on 32-bit numbers, much like the `int32` type. It returns the result of addition modulo `2^32` in a new `Field` element. | ||
|
||
The input `Field`s must not exceed 32 bits in size. You can use [rangeCheck32](#rangecheck32) to ensure this. | ||
|
||
Example: | ||
|
||
```ts | ||
let a = Field(8n); | ||
let b = Field(1n << 32n); | ||
|
||
Gadgets.addMod32(a, b).assertEquals(Field(8n)); | ||
``` | ||
|
||
## divMod32() | ||
|
||
```ts | ||
divMod32(field: Field) => { remainder: Field, quotient: Field } | ||
``` | ||
|
||
The `divMod32()` helper performs division modulo `2^32`, decomposing a `Field` element into two 32-bit limbs, `remainder` and `quotient`. It returns a tuple of two `Field` elements. | ||
|
||
The helper asserts that the input is no larger than 64 bits in size, and that both outputs are no larger than 32 bits in size. It is therefore unnecessary to perform range checks. | ||
|
||
Example: | ||
|
||
```ts | ||
let n = Field((1n << 32n) + 8n) | ||
let { remainder, quotient } = Gadgets.divMod32(n); | ||
// remainder = 8, quotient = 1 | ||
|
||
n.assertEquals(quotient.mul(1n << 32n).add(remainder)); | ||
``` | ||
|
||
## rangeCheck32() | ||
|
||
```ts | ||
rangeCheck32(x: Field) => void | ||
``` | ||
|
||
The `rangecheck32()` helper asserts that the input `Field` does not exceed 32 bits in size. Note that small, negative inputs are interpreted as large integers close to the field size and will therefore not pass the 32-bit check. To prove that a value lies in the int32 range `[-2^31, 2^31)`, you can use `rangeCheck32(x.add(1n << 31n))`. | ||
|
||
Example: | ||
|
||
```ts | ||
const x = Provable.witness(Field, () => Field(12345678n)); | ||
Gadgets.rangeCheck32(x); // successfully proves 32-bit range | ||
|
||
const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); | ||
Gadgets.rangeCheck32(xLarge); // throws an error since input exceeds 32 bits | ||
``` | ||
|
||
## rangeCheck64() | ||
|
||
```ts | ||
rangeCheck64(x: Field) => void | ||
``` | ||
|
||
The `rangecheck64()` helper asserts that the input `Field` does not exceed 64 bits in size. Note that small, negative inputs are interpreted as large integers close to the field size and will therefore not pass the 64-bit check. To prove that a value lies in the int64 range `[-2^63, 2^63)`, use `rangeCheck64(x.add(1n << 63n))`. | ||
|
||
Example: | ||
|
||
```ts | ||
const x = Provable.witness(Field, () => Field(12345678n)); | ||
Gadgets.rangeCheck64(x); // successfully proves 64-bit range | ||
|
||
const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); | ||
Gadgets.rangeCheck64(xLarge); // throws an error since input exceeds 64 bits | ||
``` | ||
|
||
## multiRangeCheck() | ||
|
||
```ts | ||
multiRangeCheck([x, y, z]: [Field, Field, Field]) => void | ||
``` | ||
|
||
The `multiRangeCheck()` helper asserts that all three input `Field`s do not exceed 88 bits in size. This is done more efficiently than the standalone range check helpers. The 3x88-bit range check supports BigInts up to 264 bits, which is enough for foreign field multiplication with moduli up to 2^259. | ||
|
||
Example: | ||
|
||
```ts | ||
const x = Provable.witness(Field, () => Field(12345678n)); | ||
const y = Provable.witness(Field, () => Field(12345678n)); | ||
const z = Provable.witness(Field, () => Field(12345678n)); | ||
const xLarge = Provable.witness(Field, () => Field(12345678901234567890123456789012345678n)); | ||
|
||
Gadgets.multiRangeCheck([x, y, z]); // succeeds | ||
Gadgets.multiRangeCheck([xLarge, y, z]); // fails | ||
``` | ||
|
||
## compactMultiRangeCheck() | ||
|
||
```ts | ||
compactMultiRangeCheck(xy: Field, z: Field) => [Field, Field, Field]; | ||
``` | ||
|
||
The `compactMultiRangeCheck()` helper is a variant of [multiRangeCheck](#multirangecheck) where the first two inputs `x` and `y` are passed in combined form `xy = x + 2^88*y`. It splits `x` and `y`, performs the range check, and returns `x`, `y` and `z` seperately. | ||
|
||
Example: | ||
|
||
```ts | ||
let [x, y, z] = Gadgets.compactMultiRangeCheck([xy, z]); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
--- | ||
title: Gadgets | ||
hide_title: true | ||
description: How gadgets work in o1js. | ||
keywords: | ||
- smart contracts | ||
- zkapp | ||
- gadgets | ||
- bitwise | ||
- o1js | ||
- blockchain | ||
--- | ||
|
||
:::info | ||
|
||
Please note that zkApp programmability is not yet available on Mina Mainnet, but zkApps can now be deployed to Berkeley Testnet. | ||
|
||
::: | ||
|
||
# Gadgets | ||
|
||
Gadgets are small, reusable low-level building blocks that simplify the process of creating new cryptographic primitives. Most gadgets build upon custom gates and act as low-level accelerators in the proof system. | ||
|
||
In o1js, you can import these provable and helper methods from the `Gadgets` namespace: | ||
|
||
- [Bitwise Operations](/zkapps/o1js/bitwise-operations) | ||
|
||
See the type declaration for [Gadgets](/zkapps/o1js-reference/modules#gadgets) in the o1js Reference documentation. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
0593b09
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
docs2 – ./
docs2-git-main-minadocs.vercel.app
docs2-minadocs.vercel.app
docs.minaprotocol.com
0593b09
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
07-oracles – ./examples/zkapps/07-oracles/oracle
07-oracles-minadocs.vercel.app
07-oracles-git-main-minadocs.vercel.app
07-oracles.vercel.app