diff --git a/src/examples/crypto/blake2b/blake2b.ts b/src/examples/crypto/blake2b/blake2b.ts new file mode 100644 index 000000000..1a824f5a5 --- /dev/null +++ b/src/examples/crypto/blake2b/blake2b.ts @@ -0,0 +1,18 @@ +import { Bytes, Gadgets, ZkProgram } from 'o1js'; + +export { BLAKE2BProgram, Bytes12 }; + +class Bytes12 extends Bytes(12) {} + +let BLAKE2BProgram = ZkProgram({ + name: 'blake2b', + publicOutput: Bytes(32), + methods: { + blake2b: { + privateInputs: [Bytes12], + async method(xs: Bytes12) { + return Gadgets.BLAKE2B.hash(xs, 32); + }, + }, + }, +}); diff --git a/src/examples/crypto/blake2b/run.ts b/src/examples/crypto/blake2b/run.ts new file mode 100644 index 000000000..2ce6c834d --- /dev/null +++ b/src/examples/crypto/blake2b/run.ts @@ -0,0 +1,23 @@ +import { Bytes12, BLAKE2BProgram } from './blake2b.js'; + +console.time('compile'); +await BLAKE2BProgram.compile(); +console.timeEnd('compile'); + +let preimage = Bytes12.fromString('hello world!'); + +console.log('blake2b rows:', (await BLAKE2BProgram.analyzeMethods()).blake2b.rows); + +console.time('prove'); +let proof = await BLAKE2BProgram.blake2b(preimage); +console.timeEnd('prove'); +let isValid = await BLAKE2BProgram.verify(proof); + +console.log('digest:', proof.publicOutput.toHex()); + +if ( + proof.publicOutput.toHex() !== + '4fccfb4d98d069558aa93e9565f997d81c33b080364efd586e77a433ddffc5e2' +) + throw new Error('Invalid blake2b digest!'); +if (!isValid) throw new Error('Invalid proof'); diff --git a/src/lib/provable/crypto/hash.ts b/src/lib/provable/crypto/hash.ts index 3c7edaf81..ad278deb3 100644 --- a/src/lib/provable/crypto/hash.ts +++ b/src/lib/provable/crypto/hash.ts @@ -138,4 +138,10 @@ const Hash = { return Keccak.preNist(512, bytes); }, }, + + BLAKE2B: { + hash(bytes: Bytes) { + return Gadgets.BLAKE2B.hash(bytes); + }, + }, }; diff --git a/src/lib/provable/gadgets/arithmetic.ts b/src/lib/provable/gadgets/arithmetic.ts index d82738219..99309bc5f 100644 --- a/src/lib/provable/gadgets/arithmetic.ts +++ b/src/lib/provable/gadgets/arithmetic.ts @@ -2,9 +2,9 @@ import { provableTuple } from '../types/struct.js'; import { Field } from '../wrapped.js'; import { assert } from '../../util/errors.js'; import { Provable } from '../provable.js'; -import { rangeCheck32, rangeCheckN } from './range-check.js'; +import { rangeCheck32, rangeCheck64, rangeCheckN } from './range-check.js'; -export { divMod32, addMod32 }; +export { divMod32, addMod32, divMod64, addMod64 }; function divMod32(n: Field, nBits = 64) { assert( @@ -56,3 +56,57 @@ function divMod32(n: Field, nBits = 64) { function addMod32(x: Field, y: Field) { return divMod32(x.add(y), 33).remainder; } + +function divMod64(n: Field, nBits = 128) { + assert( + nBits >= 0 && nBits < 255, + `nBits must be in the range [0, 255), got ${nBits}` + ); + + // calculate the number of bits allowed for the quotient to avoid overflow + const quotientBits = Math.max(0, nBits - 64); + + if (n.isConstant()) { + assert( + n.toBigInt() < 1n << BigInt(nBits), + `n needs to fit into ${nBits} bits, but got ${n.toBigInt()}` + ); + let nBigInt = n.toBigInt(); + let q = nBigInt >> 64n; + let r = nBigInt - (q << 64n); + return { + remainder: new Field(r), + quotient: new Field(q), + }; + } + + let [quotient, remainder] = Provable.witness( + provableTuple([Field, Field]), + () => { + let nBigInt = n.toBigInt(); + let q = nBigInt >> 64n; + let r = nBigInt - (q << 64n); + return [q, r] satisfies [bigint, bigint]; + } + ); + + if (quotientBits === 1) { + quotient.assertBool(); + } else if (quotientBits === 64) { + rangeCheck64(quotient); + } else { + rangeCheckN(quotientBits, quotient); + } + rangeCheck64(remainder); + + n.assertEquals(quotient.mul(1n << 64n).add(remainder)); + + return { + remainder, + quotient, + }; +} + +function addMod64(x: Field, y: Field) { + return divMod64(x.add(y), 65).remainder; +} diff --git a/src/lib/provable/gadgets/bitwise.ts b/src/lib/provable/gadgets/bitwise.ts index fdd4ec8a8..425cf54f2 100644 --- a/src/lib/provable/gadgets/bitwise.ts +++ b/src/lib/provable/gadgets/bitwise.ts @@ -13,6 +13,7 @@ export { rotate64, rotate32, and, + or, rightShift64, leftShift64, leftShift32, @@ -173,6 +174,10 @@ function and(a: Field, b: Field, length: number) { return outputAnd; } +function or(a: Field, b: Field, length: number) { + return not(and(not(a, length), not(b, length), length), length); +} + function rotate64( field: Field, bits: number, diff --git a/src/lib/provable/gadgets/blake2b.ts b/src/lib/provable/gadgets/blake2b.ts new file mode 100644 index 000000000..51fb1a3e8 --- /dev/null +++ b/src/lib/provable/gadgets/blake2b.ts @@ -0,0 +1,282 @@ +// https://datatracker.ietf.org/doc/html/rfc7693.html +import { UInt64, UInt8 } from '../int.js'; +import { FlexibleBytes } from '../bytes.js'; +import { Bytes } from '../wrapped-classes.js'; +import { Gadgets } from './gadgets.js'; +import { assert } from '../../util/errors.js'; +import { Provable } from '../provable.js'; + +export { BLAKE2B }; + +/** + * IV[0..7] Initialization Vector (constant). + * SIGMA[0..9] Message word permutations (constant). + * p[0..7] Parameter block (defines hash and key sizes). + * m[0..15] Sixteen words of a single message block. + * h[0..7] Internal state of the hash. + * d[0..dd-1] Padded input blocks. Each has "bb" bytes. + * t Message byte offset at the end of the current block. + * f Flag indicating the last block. + * + * All mathematical operations are on 64-bit words in BLAKE2b. + * + * Byte (octet) streams are interpreted as words in little-endian order, + * with the least-significant byte first. + */ + +type State = { + h: UInt64[]; + t: UInt64[]; + buf: UInt8[]; + buflen: number; + outlen: number; +}; + +const BLAKE2BConstants = { + IV: [ + UInt64.from(0x6a09e667f3bcc908n), + UInt64.from(0xbb67ae8584caa73bn), + UInt64.from(0x3c6ef372fe94f82bn), + UInt64.from(0xa54ff53a5f1d36f1n), + UInt64.from(0x510e527fade682d1n), + UInt64.from(0x9b05688c2b3e6c1fn), + UInt64.from(0x1f83d9abfb41bd6bn), + UInt64.from(0x5be0cd19137e2179n), + ], + + SIGMA: [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], + [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], + [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], + [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], + [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], + [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], + [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], + [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], + [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], + ], +}; + +const BLAKE2B = { + hash(data: FlexibleBytes, digestLength = 64) { + assert( + digestLength >= 1 && digestLength <= 64, + `digestLength must be in the range [1, 64], got ${digestLength}` + ); + assert( + data.length >= 0 && data.length < 2 ** 128, + `data byte length must be in the range [0, 2**128), got ${data.length}` + ); + const state = initialize(digestLength); + const updated_state = update(state, Bytes.from(data).bytes); + const out = final(updated_state); + return Bytes.from(out); + }, + get IV() { + return BLAKE2BConstants.IV; + }, +}; + +/** + * G function + * + * @param {UInt64[]} v + * @param {number} a + * @param {number} b + * @param {number} c + * @param {number} d + * @param {UInt64} x + * @param {UInt64} y + */ +function G( + v: UInt64[], + a: number, + b: number, + c: number, + d: number, + x: UInt64, + y: UInt64 +) { + v[a] = UInt64.Unsafe.fromField( + Gadgets.divMod64(v[a].value.add(v[b].value.add(x.value)), 128).remainder + ); + v[d] = v[d].xor(v[a]).rotate(32, 'right'); + + v[c] = UInt64.Unsafe.fromField( + Gadgets.divMod64(v[c].value.add(v[d].value), 128).remainder + ); + v[b] = v[b].xor(v[c]).rotate(24, 'right'); + + v[a] = UInt64.Unsafe.fromField( + Gadgets.divMod64(v[a].value.add(v[b].value.add(y.value)), 128).remainder + ); + v[d] = v[d].xor(v[a]).rotate(16, 'right'); + + v[c] = UInt64.Unsafe.fromField( + Gadgets.divMod64(v[c].value.add(v[d].value), 128).remainder + ); + v[b] = v[b].xor(v[c]).rotate(63, 'right'); +} + +/** + * Compression function. "last" flag indicates last block. + * @param {State} state + * @param {boolean} last + */ +function compress( + state: { + h: UInt64[]; + t: UInt64[]; + buf: UInt8[]; + buflen: number; + outlen: number; + }, + last: boolean +): State { + const { h, t, buf } = state; + const v = h.concat(BLAKE2B.IV); // initalize local work vector. First half from state and second half from IV. + + v[12] = v[12].xor(t[0]); // low word of the offset + v[13] = v[13].xor(t[1]); // high word of the offset + + if (last) { + // last block flag set ? + v[14] = v[14].not(); + } + + const m: UInt64[] = []; + for (let i = 0; i < 16; i++) { + // get little-endian words + m.push( + UInt64.Unsafe.fromField( + buf[i * 8].value + .add(buf[i * 8 + 1].value.mul(1n << 8n)) + .add(buf[i * 8 + 2].value.mul(1n << 16n)) + .add(buf[i * 8 + 3].value.mul(1n << 24n)) + .add(buf[i * 8 + 4].value.mul(1n << 32n)) + .add(buf[i * 8 + 5].value.mul(1n << 40n)) + .add(buf[i * 8 + 6].value.mul(1n << 48n)) + .add(buf[i * 8 + 7].value.mul(1n << 56n)).seal() + ) + ); + } + + for (let i = 0; i < 12; i++) { + // twelve rounds + const s = BLAKE2BConstants.SIGMA[i % 10]; + G(v, 0, 4, 8, 12, m[s[0]], m[s[1]]); + G(v, 1, 5, 9, 13, m[s[2]], m[s[3]]); + G(v, 2, 6, 10, 14, m[s[4]], m[s[5]]); + G(v, 3, 7, 11, 15, m[s[6]], m[s[7]]); + + G(v, 0, 5, 10, 15, m[s[8]], m[s[9]]); + G(v, 1, 6, 11, 12, m[s[10]], m[s[11]]); + G(v, 2, 7, 8, 13, m[s[12]], m[s[13]]); + G(v, 3, 4, 9, 14, m[s[14]], m[s[15]]); + } + + for (let i = 0; i < 8; i++) { + // XOR the two halves + h[i] = v[i].xor(v[i + 8]).xor(h[i]); + } + return state; +} + +/** + * Initializes the state with the given digest length. + * + * @param {number} outlen - Digest length in bits + * @returns {State} + */ +function initialize(outlen: number): { + h: UInt64[]; + t: UInt64[]; + buf: UInt8[]; + buflen: number; + outlen: number; +} { + const h = BLAKE2B.IV.slice(); // shallow copy IV to h + h[0] = UInt64.from(0x01010000).xor(UInt64.from(outlen)).xor(h[0]); // state "param block" + + return { + h, + t: [UInt64.zero, UInt64.zero], + buf: [], + buflen: 0, + outlen, + }; +} + +/** + * Updates hash state + * @param {State} state + * @param {UInt8[]} input + * @returns {State} updated state + */ +function update( + state: { + h: UInt64[]; + t: UInt64[]; + buf: UInt8[]; + buflen: number; + outlen: number; + }, + input: UInt8[] +): State { + for (let i = 0; i < input.length; i++) { + if (state.buflen === 128) { + // buffer full ? + state.t[0] = state.t[0].addMod64(UInt64.from(state.buflen)); // add counters + if (state.t[0].toBigInt() < state.buflen) { + // carry overflow ? + state.t[1] = state.t[1].addMod64(UInt64.one); // high word + } + state = compress(state, false); // compress (not last) + state.buflen = 0; // counter to zero + } + state.buf[state.buflen++] = input[i]; + } + return state; +} + +/** + * Finalizes the hash state and returns digest + * @param {State} state + * @returns {UInt8[]} digest + */ +function final(state: { + h: UInt64[]; + t: UInt64[]; + buf: UInt8[]; + buflen: number; + outlen: number; +}): UInt8[] { + state.t[0] = state.t[0].addMod64(UInt64.from(state.buflen)); // mark last block offset + if (state.t[0].toBigInt() < state.buflen) { + // carry overflow ? + state.t[1] = state.t[1].addMod64(UInt64.one); // high word + } + /* + state.t[1] = state.t[1].add( + Provable.if( + state.t[0].lessThan(UInt64.from(state.buflen)), + UInt64.one, + UInt64.zero + ) + ); +*/ + while (state.buflen < 128) { + state.buf[state.buflen++] = UInt8.from(0); // fill up with zeroes + } + compress(state, true); + + // little endian convert and store + const out: UInt8[] = []; + for (let i = 0; i < state.outlen; i++) { + out[i] = UInt8.from( + state.h[i >> 3].rightShift(8 * (i & 7)).and(UInt64.from(0xff)) + ); + } + return out; +} diff --git a/src/lib/provable/gadgets/gadgets.ts b/src/lib/provable/gadgets/gadgets.ts index 15c0401e7..dd775ebe1 100644 --- a/src/lib/provable/gadgets/gadgets.ts +++ b/src/lib/provable/gadgets/gadgets.ts @@ -4,12 +4,12 @@ import { compactMultiRangeCheck, multiRangeCheck, + rangeCheck8, rangeCheck16, - rangeCheck64, rangeCheck32, + rangeCheck64, rangeCheckN, isDefinitelyInRangeN, - rangeCheck8, } from './range-check.js'; import { not, @@ -17,6 +17,7 @@ import { rotate64, xor, and, + or, leftShift64, rightShift64, leftShift32, @@ -27,8 +28,9 @@ import { Field3, Sum as ForeignFieldSum, } from './foreign-field.js'; -import { divMod32, addMod32 } from './arithmetic.js'; +import { divMod32, addMod32, divMod64, addMod64 } from './arithmetic.js'; import { SHA256 } from './sha256.js'; +import { BLAKE2B } from './blake2b.js'; import { rangeCheck3x12 } from './lookup.js'; export { Gadgets, Field3, ForeignFieldSum }; @@ -418,7 +420,8 @@ const Gadgets = { * Bitwise AND gadget on {@link Field} elements. Equivalent to the [bitwise AND `&` operator in JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_AND). * The AND gate works by comparing two bits and returning `1` if both bits are `1`, and `0` otherwise. * - * It can be checked by a double generic gate that verifies the following relationship between the values below (in the process it also invokes the {@link Gadgets.xor} gadget which will create additional constraints depending on `length`). + * It can be checked by a double generic gate that verifies the following relationship between the values + * below (in the process it also invokes the {@link Gadgets.xor} gadget which will create additional constraints depending on `length`). * * The generic gate verifies:\ * `a + b = sum` and the conjunction equation `2 * and = sum - xor`\ @@ -429,7 +432,9 @@ const Gadgets = { * * You can find more details about the implementation in the [Mina book](https://o1-labs.github.io/proof-systems/specs/kimchi.html?highlight=gates#and) * - * The `length` parameter lets you define how many bits should be compared. `length` is rounded to the nearest multiple of 16, `paddedLength = ceil(length / 16) * 16`, and both input values are constrained to fit into `paddedLength` bits. The output is guaranteed to have at most `paddedLength` bits as well. + * The `length` parameter lets you define how many bits should be compared. `length` is rounded + * to the nearest multiple of 16, `paddedLength = ceil(length / 16) * 16`, and both input values + * are constrained to fit into `paddedLength` bits. The output is guaranteed to have at most `paddedLength` bits as well. * * **Note:** Specifying a larger `length` parameter adds additional constraints. * @@ -448,6 +453,31 @@ const Gadgets = { and(a: Field, b: Field, length: number) { return and(a, b, length); }, + /** + * Bitwise OR gadget on {@link Field} elements. Equivalent to the [bitwise OR `|` operator in JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_OR). + * The OR gate works by comparing two bits and returning `1` if at least one bit is `1`, and `0` otherwise. + * + * The `length` parameter lets you define how many bits should be compared. `length` is rounded + * to the nearest multiple of 16, `paddedLength = ceil(length / 16) * 16`, and both input values + * are constrained to fit into `paddedLength` bits. The output is guaranteed to have at most `paddedLength` bits as well. + * + * **Note:** Specifying a larger `length` parameter adds additional constraints. + * + * **Note:** Both {@link Field} elements need to fit into `2^paddedLength - 1`. Otherwise, an error is thrown and no proof can be generated. + * For example, with `length = 2` (`paddedLength = 16`), `and()` will fail for any input that is larger than `2**16`. + * + * @example + * ```typescript + * let a = Field.from(3); // ... 000011 + * let b = Field.from(5); // ... 000101 + * + * let c = Gadgets.or(a, b, 16); // ... 000111 + * c.assertEquals(7); + * ``` + */ + or(a: Field, b: Field, length: number) { + return or(a, b, length); + }, /** * Multi-range check. @@ -867,6 +897,43 @@ const Gadgets = { * */ addMod32, + /** + * Division modulo 2^64. The operation decomposes a {@link Field} element in the range [0, 2^128) into two 64-bit limbs, `remainder` and `quotient`, using the following equation: `n = quotient * 2^64 + remainder`. + * + * **Note:** The gadget acts as a proof that the input is in the range [0, 2^128). If the input exceeds 128 bits, the gadget fails. + * + * Asserts that both `remainder` and `quotient` are in the range [0, 2^64) using {@link Gadgets.rangeCheck64}. + * + * @example + * ```ts + * let n = Field((1n << 64n) + 8n) + * let { remainder, quotient } = Gadgets.divMod64(n); + * // remainder = 8, quotient = 1 + * + * n.assertEquals(quotient.mul(1n << 64n).add(remainder)); + * ``` + */ + divMod64, + + /** + * Addition modulo 2^64. The operation adds two {@link Field} elements in the range [0, 2^128] and returns the result modulo 2^64. + * + * Asserts that the result is in the range [0, 2^64) using {@link Gadgets.rangeCheck64}. + * + * It uses {@link Gadgets.divMod64} internally by adding the two {@link Field} elements and then decomposing the result into `remainder` and `quotient` and returning the `remainder`. + * + * **Note:** The gadget assumes both inputs to be in the range [0, 2^64). When called with non-range-checked inputs, be aware that the sum `a + b` can overflow the native field and the gadget can succeed but return an invalid result. + * + * @example + * ```ts + * let a = Field(8n); + * let b = Field(1n << 64n); + * + * Gadgets.addMod64(a, b).assertEquals(Field(8n)); + * ``` + */ + addMod64, + /** * Implementation of the [SHA256 hash function.](https://en.wikipedia.org/wiki/SHA-2) Hash function with 256bit output. * @@ -886,4 +953,24 @@ const Gadgets = { * */ SHA256: SHA256, + + /** + * Implementation of the [BLAKE2b hash function.](https://en.wikipedia.org/wiki/BLAKE_(hash_function)#BLAKE2) Hash function with arbitrary length output. + * + * Applies the BLAKE2b hash function to a list of byte-sized elements. + * + * The function accepts {@link Bytes} as the input message, which is a type that represents a static-length list of byte-sized field elements (range-checked using {@link Gadgets.rangeCheck8}). + * Alternatively, you can pass plain `number[]`, `bigint[]` or `Uint8Array` to perform a hash outside provable code. + * + * Produces an output of {@link Bytes} that conforms to the chosen digest length. + * + * @param data - {@link Bytes} representing the message to hash. + * + * ```ts + * let preimage = Bytes.fromString("hello world"); + * let digest = Gadgets.BLAKE2b.hash(preimage); + * ``` + * + */ + BLAKE2B: BLAKE2B, }; diff --git a/src/lib/provable/int.ts b/src/lib/provable/int.ts index 7652e7172..c8297faeb 100644 --- a/src/lib/provable/int.ts +++ b/src/lib/provable/int.ts @@ -6,7 +6,7 @@ import { HashInput } from './crypto/poseidon.js'; import { Provable } from './provable.js'; import * as RangeCheck from './gadgets/range-check.js'; import * as Bitwise from './gadgets/bitwise.js'; -import { addMod32 } from './gadgets/arithmetic.js'; +import { addMod32, addMod64 } from './gadgets/arithmetic.js'; import type { Gadgets } from './gadgets/gadgets.js'; import { withMessage } from './field.js'; import { FieldVar } from './core/fieldvar.js'; @@ -155,6 +155,13 @@ class UInt64 extends CircuitValue { return new UInt64((1n << 64n) - 1n); } + /** + * Addition modulo 2^64. Check {@link Gadgets.addMod64} for a detailed description. + */ + addMod64(y: UInt64) { + return new UInt64(addMod64(this.value, y.value).value); + } + /** * Integer division with remainder. * @@ -403,6 +410,23 @@ class UInt64 extends CircuitValue { return new UInt64(Bitwise.and(this.value, x.value, UInt64.NUM_BITS).value); } + /** + * Bitwise OR gadget on {@link UInt64} elements. Equivalent to the [bitwise OR `|` operator in JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_OR). + * The OR gate works by comparing two bits and returning `1` if at least one bit is `1`, and `0` otherwise. + * + * @example + * ```typescript + * let a = UInt64.from(3); // ... 000011 + * let b = UInt64.from(5); // ... 000101 + * + * let c = a.or(b); // ... 000111 + * c.assertEquals(7); + * ``` + */ + or(x: UInt64) { + return new UInt64(Bitwise.or(this.value, x.value, UInt64.NUM_BITS).value); + } + /** * Checks if a {@link UInt64} is less than or equal to another one. */ @@ -863,6 +887,23 @@ class UInt32 extends CircuitValue { return new UInt32(Bitwise.and(this.value, x.value, UInt32.NUM_BITS).value); } + /** + * Bitwise OR gadget on {@link UInt32} elements. Equivalent to the [bitwise OR `|` operator in JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_OR). + * The OR gate works by comparing two bits and returning `1` if at least one bit is `1`, and `0` otherwise. + * + * @example + * ```typescript + * let a = UInt32.from(3); // ... 000011 + * let b = UInt32.from(5); // ... 000101 + * + * let c = a.or(b); // ... 000111 + * c.assertEquals(7); + * ``` + */ + or(x: UInt32) { + return new UInt32(Bitwise.or(this.value, x.value, UInt32.NUM_BITS).value); + } + /** * Checks if a {@link UInt32} is less than or equal to another one. */ @@ -1387,6 +1428,19 @@ class UInt8 extends Struct({ }, }; + /** + * Static method to create a {@link UInt8} with value `0`. + */ + static get zero() { + return new UInt8(0); + } + /** + * Static method to create a {@link UInt8} with value `1`. + */ + static get one() { + return new UInt8(1); + } + /** * Add a {@link UInt8} to another {@link UInt8} without allowing overflow. * diff --git a/src/lib/provable/test/arithmetic.unit-test.ts b/src/lib/provable/test/arithmetic.unit-test.ts index 3ce9ce07f..deafb53a9 100644 --- a/src/lib/provable/test/arithmetic.unit-test.ts +++ b/src/lib/provable/test/arithmetic.unit-test.ts @@ -20,6 +20,12 @@ let Arithmetic = ZkProgram({ return Gadgets.divMod32(a); }, }, + divMod64: { + privateInputs: [Field], + async method(a: Field) { + return Gadgets.divMod64(a); + }, + }, }, }); @@ -30,11 +36,20 @@ const divMod32Helper = (x: bigint) => { let remainder = x - (quotient << 32n); return { remainder, quotient }; }; -const divMod32Output = record({ remainder: field, quotient: field }); + +const divMod64Helper = (x: bigint) => { + let quotient = x >> 64n; + let remainder = x - (quotient << 64n); + return { remainder, quotient }; +}; + +const divModOutput = record({ remainder: field, quotient: field }); + + equivalent({ from: [field], - to: divMod32Output, + to: divModOutput, })( (x) => { assert(x < 1n << 64n, `x needs to fit in 64bit, but got ${x}`); @@ -45,7 +60,20 @@ equivalent({ } ); -await equivalentAsync({ from: [field], to: divMod32Output }, { runs: 3 })( +equivalent({ + from: [field], + to: divModOutput, +})( + (x) => { + assert(x < 1n << 128n, `x needs to fit in 128bit, but got ${x}`); + return divMod64Helper(x); + }, + (x) => { + return Gadgets.divMod64(x); + } +); + +await equivalentAsync({ from: [field], to: divModOutput }, { runs: 3 })( (x) => { assert(x < 1n << 64n, `x needs to fit in 64bit, but got ${x}`); return divMod32Helper(x); @@ -54,3 +82,13 @@ await equivalentAsync({ from: [field], to: divMod32Output }, { runs: 3 })( return (await Arithmetic.divMod32(x)).publicOutput; } ); + +await equivalentAsync({ from: [field], to: divModOutput }, { runs: 3 })( + (x) => { + assert(x < 1n << 128n, `x needs to fit in 128bit, but got ${x}`); + return divMod64Helper(x); + }, + async (x) => { + return (await Arithmetic.divMod64(x)).publicOutput; + } +); diff --git a/src/lib/provable/test/bitwise.unit-test.ts b/src/lib/provable/test/bitwise.unit-test.ts index 23b3c38bc..7486f159e 100644 --- a/src/lib/provable/test/bitwise.unit-test.ts +++ b/src/lib/provable/test/bitwise.unit-test.ts @@ -1,7 +1,6 @@ import { ZkProgram } from '../../proof-system/zkprogram.js'; import { equivalentProvable as equivalent, - equivalentAsync, field, fieldWithRng, } from '../../testing/equivalent.js'; @@ -20,13 +19,6 @@ import { } from '../../testing/constraint-system.js'; import { GateType } from '../../../snarky.js'; -const maybeField = { - ...field, - rng: Random.map(Random.oneOf(Random.field, Random.field.invalid), (x) => - mod(x, Field.ORDER) - ), -}; - let uint = (length: number) => fieldWithRng(Random.biguint(length)); let Bitwise = ZkProgram({ @@ -57,6 +49,12 @@ let Bitwise = ZkProgram({ return Gadgets.and(a, b, 64); }, }, + or: { + privateInputs: [Field, Field], + async method(a: Field, b: Field) { + return Gadgets.or(a, b, 64); + }, + }, rot32: { privateInputs: [Field], async method(a: Field) { @@ -101,6 +99,10 @@ await Bitwise.compile(); (x, y) => x & y, (x, y) => Gadgets.and(x, y, length) ); + equivalent({ from: [uint(length), uint(length)], to: field })( + (x, y) => x | y, + (x, y) => Gadgets.or(x, y, length) + ); // NOT unchecked equivalent({ from: [uint(length)], to: field })( (x) => Fp.not(x, length), @@ -139,104 +141,6 @@ await Bitwise.compile(); ); }); -const runs = 2; - -await equivalentAsync({ from: [uint(64), uint(64)], to: field }, { runs })( - (x, y) => { - return x ^ y; - }, - async (x, y) => { - let proof = await Bitwise.xor(x, y); - return proof.publicOutput; - } -); - -await equivalentAsync({ from: [maybeField], to: field }, { runs })( - (x) => { - return Fp.not(x, 240); - }, - async (x) => { - let proof = await Bitwise.notUnchecked(x); - return proof.publicOutput; - } -); -await equivalentAsync({ from: [maybeField], to: field }, { runs })( - (x) => { - if (x > 2n ** 240n) throw Error('Does not fit into 240 bit'); - return Fp.not(x, 240); - }, - async (x) => { - let proof = await Bitwise.notChecked(x); - return proof.publicOutput; - } -); - -await equivalentAsync({ from: [maybeField, maybeField], to: field }, { runs })( - (x, y) => { - if (x >= 2n ** 64n || y >= 2n ** 64n) - throw Error('Does not fit into 64 bits'); - return x & y; - }, - async (x, y) => { - let proof = await Bitwise.and(x, y); - return proof.publicOutput; - } -); - -await equivalentAsync({ from: [field], to: field }, { runs })( - (x) => { - if (x >= 2n ** 64n) throw Error('Does not fit into 64 bits'); - return Fp.rot(x, 12n, 'left'); - }, - async (x) => { - let proof = await Bitwise.rot64(x); - return proof.publicOutput; - } -); - -await equivalentAsync({ from: [uint(32)], to: uint(32) }, { runs })( - (x) => { - return Fp.rot(x, 12n, 'left', 32n); - }, - async (x) => { - let proof = await Bitwise.rot32(x); - return proof.publicOutput; - } -); - -await equivalentAsync({ from: [field], to: field }, { runs })( - (x) => { - if (x >= 2n ** 64n) throw Error('Does not fit into 64 bits'); - return Fp.leftShift(x, 12); - }, - async (x) => { - let proof = await Bitwise.leftShift64(x); - return proof.publicOutput; - } -); - -await equivalentAsync({ from: [field], to: field }, { runs })( - (x) => { - if (x >= 1n << 32n) throw Error('Does not fit into 32 bits'); - return Fp.leftShift(x, 12, 32); - }, - async (x) => { - let proof = await Bitwise.leftShift32(x); - return proof.publicOutput; - } -); - -await equivalentAsync({ from: [field], to: field }, { runs })( - (x) => { - if (x >= 2n ** 64n) throw Error('Does not fit into 64 bits'); - return Fp.rightShift(x, 12); - }, - async (x) => { - let proof = await Bitwise.rightShift64(x); - return proof.publicOutput; - } -); - // check that gate chains stay intact function xorChain(bits: number) { @@ -267,6 +171,12 @@ constraintSystem.fromZkProgram( ifNotAllConstant(contains(xorChain(64))) ); +constraintSystem.fromZkProgram( + Bitwise, + 'or', + ifNotAllConstant(contains(xorChain(64))) +); + let rotChain: GateType[] = ['Rot64', 'RangeCheck0']; let isJustRotate = ifNotAllConstant( and(contains(rotChain), withoutGenerics(equals(rotChain))) diff --git a/src/lib/provable/test/blake2b.unit-test.ts b/src/lib/provable/test/blake2b.unit-test.ts new file mode 100644 index 000000000..d45a6341e --- /dev/null +++ b/src/lib/provable/test/blake2b.unit-test.ts @@ -0,0 +1,89 @@ +import { ZkProgram } from '../../proof-system/zkprogram.js'; +import { Bytes } from '../wrapped-classes.js'; +import { Gadgets } from '../gadgets/gadgets.js'; +import { blake2b as nobleBlake2b } from '@noble/hashes/blake2b'; +import { bytes } from './test-utils.js'; +import { + equivalentAsync, + equivalentProvable, +} from '../../testing/equivalent.js'; +import { Random, sample } from '../../testing/random.js'; +import { expect } from 'expect'; + + + +for (let { digest_length ,preimage, hash } of testVectors()) { + let actual = Gadgets.BLAKE2B.hash(Bytes.fromString(preimage), digest_length); + expect(actual.toHex()).toEqual(hash); +} + + +function testVectors() { + return [ + { + digest_length: 32, + preimage: 'The quick brown fox jumps over the lazy dog', + hash: '01718cec35cd3d796dd00020e0bfecb473ad23457d063b75eff29c0ffa2e58a9' + }, + { + digest_length: 32, + preimage: 'o1js', + hash: '2b8198c64ceccbf2863835a07be336da04479cf0bd460565526e916367b77988', + }, + { + digest_length: 32, + preimage: '', + hash: '0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8' + }, + { + digest_length: 32, + preimage: '0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b', + hash: 'fa30f36c826e6bf4531e79e01f12e7f773e7ab62bd1ffd38cf0e950eaf5c0434' + }, + { + digest_length: 32, + preimage: '4a656665', + hash: 'b009ba3a88866add8b55de6cc8c040370a09022dc3f80afce090d81b0d8eded4', + }, + { + digest_length: 32, + preimage: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + hash: '5c3c8bdd63c262d014180d9d54d797946c921fa65f02703c33bca7062e1829c1' + }, + { + digest_length: 32, + preimage: 'Lorem ipsum odor amet, consectetuer adipiscing elit. Laoreet purus himenaeos blandit lectus blandit porta leo rutrum dui sociosqu sit aliquet ad finibus consectetur quam justo finibus aptent molestie dapibus integer tellus nisi habitasse nascetur molestie mi etiam mauris habitant integer metus euismod adipiscing pretium fames massa massa habitant nulla himenaeos maximus porta natoque imperdiet metus ullamcorper auctor faucibus sit magna maximus ac at integer litora nam sem varius laoreet auctor magna quis metus sapien nam porttitor metus nascetur malesuada sapien aenean iaculis nisl tellus vel montes netus feugiat mi sollicitudin malesuada convallis semper tellus posuere erat phasellus est pharetra eleifend a maximus pharetra ultrices fames luctus vehicula egestas mollis hendrerit natoque pharetra congue fringilla dictum proin volutpat suspendisse leo volutpat odio ut egestas tellus ad lectus volutpat rutrum a hac dictumst rutrum nulla nullam magnis ridiculus maecenas nascetur curabitur conubia faucibus ac integer hendrerit sodales primis neque lacus ad blandit dapibus maximus sapien sapien felis potenti tempus rutrum augue metus ante nunc vulputate interdum dignissim cursus finibus malesuada elementum scelerisque lacinia habitasse aenean sociosqu dis facilisi integer ut elementum fames class penatibus sociosqu curabitur sed dui nostra odio ut dis etiam torquent velit porta conubia adipiscing taciti in est parturient nulla vestibulum ultricies vivamus sed inceptos morbi sem inceptos odio diam lectus massa pellentesque ligula dolor maximus venenatis varius nec dapibus faucibus ultrices penatibus rutrum montes curabitur euismod non nibh cursus quis ad diam quisque nisi scelerisque nibh placerat neque ut vitae leo vehicula mi donec phasellus dapibus ornare morbi lobortis maximus metus nullam maecenas dui hendrerit faucibus porttitor phasellus metus scelerisque hendrerit montes sollicitudin vehicula erat iaculis nisl blandit maecenas natoque finibus lorem maecenas fringilla posuere curae tempor torquent quis dapibus elementum hendrerit auctor torquent aliquam lobortis montes et aliquet aliquet adipiscing eros nisl nec porttitor a mattis ornare pharetra mauris aliquam suspendisse tincidunt nascetur sollicitudin porttitor vulputate donec imperdiet vel elementum scelerisque; cras a lacinia nullam consectetur natoque non integer cras duis eu torquent efficitur cursus bibendum torquent amet volutpat senectus eleifend ipsum consequat per tortor scelerisque eget mus facilisi sem habitant aliquam sodales arcu ultricies suspendisse mi dolor augue laoreet porttitor enim ullamcorper class varius enim posuere eros suspendisse fusce ex luctus lobortis duis vulputate quis quam porta dolor scelerisque porttitor aliquet congue varius primis nunc faucibus vestibulum erat faucibus neque himenaeos fusce euismod quis in feugiat curabitur arcu varius ullamcorper aenean lacus pulvinar odio a aliquet consequat interdum faucibus bibendum tempus ad augue taciti enim nisi nisl dui duis sem nunc tempus maecenas suspendisse porta senectus enim vivamus elit sapien imperdiet placerat nec inceptos malesuada egestas consectetur laoreet iaculis enim ac aenean odio duis malesuada tincidunt eros mattis iaculis montes eu felis penatibus consequat egestas quis fermentum ultricies tortor suscipit sagittis nisi justo dui dolor adipiscing magna id scelerisque eu sollicitudin class lobortis donec conubia morbi luctus donec natoque velit magna sit natoque ultricies natoque curabitur eu a mattis fermentum facilisi libero sapien pharetra tellus vitae ultrices a elit egestas posuere pharetra fames nascetur ex aenean ligula non amet ligula felis accumsan felis velit class accumsan dis non mus faucibus posuere nascetur. Lorem ipsum odor amet, consectetuer adipiscing elit. Laoreet purus himenaeos blandit lectus blandit porta leo rutrum dui sociosqu sit aliquet ad finibus consectetur quam justo finibus aptent molestie dapibus integer tellus nisi habitasse nascetur molestie mi etiam mauris habitant integer metus euismod adipiscing pretium fames massa massa habitant nulla himenaeos maximus porta natoque imperdiet metus ullamcorper auctor faucibus sit magna maximus ac at integer litora nam sem varius laoreet auctor magna quis metus sapien nam porttitor metus nascetur malesuada sapien aenean iaculis nisl tellus vel montes netus feugiat mi sollicitudin malesuada convallis semper tellus posuere erat phasellus est pharetra eleifend a maximus pharetra ultrices fames luctus vehicula egestas mollis hendrerit natoque pharetra congue fringilla dictum proin volutpat suspendisse leo volutpat odio ut egestas tellus ad lectus volutpat rutrum a hac dictumst rutrum nulla nullam magnis ridiculus maecenas nascetur curabitur conubia faucibus ac integer hendrerit sodales primis neque lacus ad blandit dapibus maximus sapien sapien felis potenti tempus rutrum augue metus ante nunc vulputate interdum dignissim cursus finibus malesuada elementum scelerisque lacinia habitasse aenean sociosqu dis facilisi integer ut elementum fames class penatibus sociosqu curabitur sed dui nostra odio ut dis etiam torquent velit porta conubia adipiscing taciti in est parturient nulla vestibulum ultricies vivamus sed inceptos morbi sem inceptos odio diam lectus massa pellentesque ligula dolor maximus venenatis varius nec dapibus faucibus ultrices penatibus rutrum montes curabitur euismod non nibh cursus quis ad diam quisque nisi scelerisque nibh placerat neque ut vitae leo vehicula mi donec phasellus dapibus ornare morbi lobortis maximus metus nullam maecenas dui hendrerit faucibus porttitor phasellus metus scelerisque hendrerit montes sollicitudin vehicula erat iaculis nisl blandit maecenas natoque finibus lorem maecenas fringilla posuere curae tempor torquent quis dapibus elementum hendrerit auctor torquent aliquam lobortis montes et aliquet aliquet adipiscing eros nisl nec porttitor a mattis ornare pharetra mauris aliquam suspendisse tincidunt nascetur sollicitudin porttitor vulputate donec imperdiet vel elementum scelerisque; cras a lacinia nullam consectetur natoque non integer cras duis eu torquent efficitur cursus bibendum torquent amet volutpat senectus eleifend ipsum consequat per tortor scelerisque eget mus facilisi sem habitant aliquam sodales arcu ultricies suspendisse mi dolor augue laoreet porttitor enim ullamcorper class varius enim posuere eros suspendisse fusce ex luctus lobortis duis vulputate quis quam porta dolor scelerisque porttitor aliquet congue varius primis nunc faucibus vestibulum erat faucibus neque himenaeos fusce euismod quis in feugiat curabitur arcu varius ullamcorper aenean lacus pulvinar odio a aliquet consequat interdum faucibus bibendum tempus ad augue taciti enim nisi nisl dui duis sem nunc tempus maecenas suspendisse porta senectus enim vivamus elit sapien imperdiet placerat nec inceptos malesuada egestas consectetur laoreet iaculis enim ac aenean odio duis malesuada tincidunt eros mattis iaculis montes eu felis penatibus consequat egestas quis fermentum ultricies tortor suscipit sagittis nisi justo dui dolor adipiscing magna id scelerisque eu sollicitudin class lobortis donec conubia morbi luctus donec natoque velit magna sit natoque ultricies natoque curabitur eu a mattis fermentum facilisi libero sapien pharetra tellus vitae ultrices a elit egestas posuere pharetra fames nascetur ex aenean ligula non amet ligula felis accumsan felis velit class accumsan dis non mus faucibus posuere nascetur. Lorem ipsum odor amet, consectetuer adipiscing elit. Laoreet purus himenaeos blandit lectus blandit porta leo rutrum dui sociosqu sit aliquet ad finibus consectetur quam justo finibus aptent molestie dapibus integer tellus nisi habitasse nascetur molestie mi etiam mauris habitant integer metus euismod adipiscing pretium fames massa massa habitant nulla himenaeos maximus porta natoque imperdiet metus ullamcorper auctor faucibus sit magna maximus ac at integer litora nam sem varius laoreet auctor magna quis metus sapien nam porttitor metus nascetur malesuada sapien aenean iaculis nisl tellus vel montes netus feugiat mi sollicitudin malesuada convallis semper tellus posuere erat phasellus est pharetra eleifend a maximus pharetra ultrices fames luctus vehicula egestas mollis hendrerit natoque pharetra congue fringilla dictum proin volutpat suspendisse leo volutpat odio ut egestas tellus ad lectus volutpat rutrum a hac dictumst rutrum nulla nullam magnis ridiculus maecenas nascetur curabitur conubia faucibus ac integer hendrerit sodales primis neque lacus ad blandit dapibus maximus sapien sapien felis potenti tempus rutrum augue metus ante nunc vulputate interdum dignissim cursus finibus malesuada elementum scelerisque lacinia habitasse aenean sociosqu dis facilisi integer ut elementum fames class penatibus sociosqu curabitur sed dui nostra odio ut dis etiam torquent velit porta conubia adipiscing taciti in est parturient nulla vestibulum ultricies vivamus sed inceptos morbi sem inceptos odio diam lectus massa pellentesque ligula dolor maximus venenatis varius nec dapibus faucibus ultrices penatibus rutrum montes curabitur euismod non nibh cursus quis ad diam quisque nisi scelerisque nibh placerat neque ut vitae leo vehicula mi donec phasellus dapibus ornare morbi lobortis maximus metus nullam maecenas dui hendrerit faucibus porttitor phasellus metus scelerisque hendrerit montes sollicitudin vehicula erat iaculis nisl blandit maecenas natoque finibus lorem maecenas fringilla posuere curae tempor torquent quis dapibus elementum hendrerit auctor torquent aliquam lobortis montes et aliquet aliquet adipiscing eros nisl nec porttitor a mattis ornare pharetra mauris aliquam suspendisse tincidunt nascetur sollicitudin porttitor vulputate donec imperdiet vel elementum scelerisque; cras a lacinia nullam consectetur natoque non integer cras duis eu torquent efficitur cursus bibendum torquent amet volutpat senectus eleifend ipsum consequat per tortor scelerisque eget mus facilisi sem habitant aliquam sodales arcu ultricies suspendisse mi dolor augue laoreet porttitor enim ullamcorper class varius enim posuere eros suspendisse fusce ex luctus lobortis duis vulputate quis quam porta dolor scelerisque porttitor aliquet congue varius primis nunc faucibus vestibulum erat faucibus neque himenaeos fusce euismod quis in feugiat curabitur arcu varius ullamcorper aenean lacus pulvinar odio a aliquet consequat interdum faucibus bibendum tempus ad augue taciti enim nisi nisl dui duis sem nunc tempus maecenas suspendisse porta senectus enim vivamus elit sapien imperdiet placerat nec inceptos malesuada egestas consectetur laoreet iaculis enim ac aenean odio duis malesuada tincidunt eros mattis iaculis montes eu felis penatibus consequat egestas quis fermentum ultricies tortor suscipit sagittis nisi justo dui dolor adipiscing magna id scelerisque eu sollicitudin class lobortis donec conubia morbi luctus donec natoque velit magna sit natoque ultricies natoque curabitur eu a mattis fermentum facilisi libero sapien pharetra tellus vitae ultrices a elit egestas posuere pharetra fames nascetur ex aenean ligula non amet ligula felis accumsan felis velit class accumsan dis non mus faucibus posuere nascetur. Lorem ipsum odor amet, consectetuer adipiscing elit. Laoreet purus himenaeos blandit lectus blandit porta leo rutrum dui sociosqu sit aliquet ad finibus consectetur quam justo finibus aptent molestie dapibus integer tellus nisi habitasse nascetur molestie mi etiam mauris habitant integer metus euismod adipiscing pretium fames massa massa habitant nulla himenaeos maximus porta natoque imperdiet metus ullamcorper auctor faucibus sit magna maximus ac at integer litora nam sem varius laoreet auctor magna quis metus sapien nam porttitor metus nascetur malesuada sapien aenean iaculis nisl tellus vel montes netus feugiat mi sollicitudin malesuada convallis semper tellus posuere erat phasellus est pharetra eleifend a maximus pharetra ultrices fames luctus vehicula egestas mollis hendrerit natoque pharetra congue fringilla dictum proin volutpat suspendisse leo volutpat odio ut egestas tellus ad lectus volutpat rutrum a hac dictumst rutrum nulla nullam magnis ridiculus maecenas nascetur curabitur conubia faucibus ac integer hendrerit sodales primis neque lacus ad blandit dapibus maximus sapien sapien felis potenti tempus rutrum augue metus ante nunc vulputate interdum dignissim cursus finibus malesuada elementum scelerisque lacinia habitasse aenean sociosqu dis facilisi integer ut elementum fames class penatibus sociosqu curabitur sed dui nostra odio ut dis etiam torquent velit porta conubia adipiscing taciti in est parturient nulla vestibulum ultricies vivamus sed inceptos morbi sem inceptos odio diam lectus massa pellentesque ligula dolor maximus venenatis varius nec dapibus faucibus ultrices penatibus rutrum montes curabitur euismod non nibh cursus quis ad diam quisque nisi scelerisque nibh placerat neque ut vitae leo vehicula mi donec phasellus dapibus ornare morbi lobortis maximus metus nullam maecenas dui hendrerit faucibus porttitor phasellus metus scelerisque hendrerit montes sollicitudin vehicula erat iaculis nisl blandit maecenas natoque finibus lorem maecenas fringilla posuere curae tempor torquent quis dapibus elementum hendrerit auctor torquent aliquam lobortis montes et aliquet aliquet adipiscing eros nisl nec porttitor a mattis ornare pharetra mauris aliquam suspendisse tincidunt nascetur sollicitudin porttitor vulputate donec imperdiet vel elementum scelerisque; cras a lacinia nullam consectetur natoque non integer cras duis eu torquent efficitur cursus bibendum torquent amet volutpat senectus eleifend ipsum consequat per tortor scelerisque eget mus facilisi sem habitant aliquam sodales arcu ultricies suspendisse mi dolor augue laoreet porttitor enim ullamcorper class varius enim posuere eros suspendisse fusce ex luctus lobortis duis vulputate quis quam porta dolor scelerisque porttitor aliquet congue varius primis nunc faucibus vestibulum erat faucibus neque himenaeos fusce euismod quis in feugiat curabitur arcu varius ullamcorper aenean lacus pulvinar odio a aliquet consequat interdum faucibus bibendum tempus ad augue taciti enim nisi nisl dui duis sem nunc tempus maecenas suspendisse porta senectus enim vivamus elit sapien imperdiet placerat nec inceptos malesuada egestas consectetur laoreet iaculis enim ac aenean odio duis malesuada tincidunt eros mattis iaculis montes eu felis penatibus consequat egestas quis fermentum ultricies tortor suscipit sagittis nisi justo dui dolor adipiscing magna id scelerisque eu sollicitudin class lobortis donec conubia morbi luctus donec natoque velit magna sit natoque ultricies natoque curabitur eu a mattis fermentum facilisi libero sapien pharetra tellus vitae ultrices a elit egestas posuere pharetra fames nascetur ex aenean ligula non amet ligula felis accumsan felis velit class accumsan dis non mus faucibus posuere nascetur.', + hash: '04c03f5291f1cf01e26cfb542070124b84fec189b3aac443c187ca6a3f708a36' + }, + { + digest_length: 64, + preimage: 'The quick brown fox jumps over the lazy dog', + hash: 'a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918' + }, + { + digest_length: 64, + preimage: 'o1js', + hash: 'abe5652d5c204163a4b418b33577b8ccebd5a5ed3d8cbb9781e7ea4a1bc3344c9e5e5707112d656f642927a42d34f96439f68a1ff0a2aa621a3f1fbb2521b18f', + }, + { + digest_length: 64, + preimage: '', + hash: '786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce' + }, + { + digest_length: 64, + preimage: '0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b', + hash: 'df2dc4b30ffd789f70735464374974de018318e5fc1343ae23d38ecfc3cba32ce81db8f831d86da2d047dd76fd295a268a99ff7c890239b2cdc207357b723c92' + }, + { + digest_length: 64, + preimage: '4a656665', + hash: 'f56d7da03e8a385777bef77834ded67cbafba64c6fc32887e6bdcc36774b6fe398ddd1bb6b77d4529cfd709ca9973e4a5d2635f9a6d3abaea7e0a44655ac7a9b', + }, + { + digest_length: 64, + preimage: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + hash: '0d5cf4c5c6e55752eb16eeda9052158fe59c964f9e9cef77c68580a65d40904f5c3639101d48a95001568a21ae6cfe0b0b405fb3d4f77255f308ec0eb07bc35a' + } + ]; +} diff --git a/tests/vk-regression/plain-constraint-system.ts b/tests/vk-regression/plain-constraint-system.ts index b0030807d..cb5375401 100644 --- a/tests/vk-regression/plain-constraint-system.ts +++ b/tests/vk-regression/plain-constraint-system.ts @@ -100,6 +100,14 @@ const BitwiseCS = constraintSystem('Bitwise Primitive', { Gadgets.and(a, b, 48); Gadgets.and(a, b, 64); }, + or() { + let a = Provable.witness(Field, () => new Field(5n)); + let b = Provable.witness(Field, () => new Field(5n)); + Gadgets.or(a, b, 16); + Gadgets.or(a, b, 32); + Gadgets.or(a, b, 48); + Gadgets.or(a, b, 64); + } }); const Bytes32 = Bytes(32); @@ -132,6 +140,11 @@ const HashCS = constraintSystem('Hashes', { ); Hash.Poseidon.hash(xs); }, + + BLAKE2B() { + let xs = Provable.witness(Bytes32, () => bytes32); + Hash.BLAKE2B.hash(xs); + } }); const witness = () => Provable.witness(Field, () => Field(0)); diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index d2e6e4894..5f4df7449 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -175,6 +175,10 @@ "and": { "rows": 19, "digest": "647e6fd1852873d1c326ba1cd269cff2" + }, + "or": { + "rows": 25, + "digest": "8b43e6612b329bf012a3e056f933625d" } }, "verificationKey": { @@ -204,6 +208,10 @@ "Poseidon": { "rows": 208, "digest": "afa1f9920f1f657ab015c02f9b2f6c52" + }, + "BLAKE2B": { + "rows": 6012, + "digest": "85a6d64774a513dbff3dd6a8fc3db76c" } }, "verificationKey": { @@ -300,6 +308,19 @@ "hash": "14970780915659850828924502346680433225700971894695273700497112528587195815959" } }, + "blake2b": { + "digest": "3d1be0b145513226d94cf098242446abf4ce399c0b75556e3f638fe5fcedd59", + "methods": { + "blake2b": { + "rows": 5408, + "digest": "ff73fb592262ef3a6b665a803052348f" + } + }, + "verificationKey": { + "data": "AABGuUK/LF9vl6gvnxbhLIfHR/lGxdSBIrr2H1Pos4RwNXJhmWu8TE6yUSkOWCpwlIFHmdsm6o9/HroYuXJej9kQXsTQ5gG8UUkS+mGS+K4ksZEBRC/1mavPExktP62UgDorcitvVNv7X7RWyxFw+zyLWEUF1KDw0DRt/7jH3MB6HhdQW899wDPnioPvzD8KhsqBc7/pW3rgMrq0Tr2l+/cawpzvy8ZotXkd2R2DzvEpLlfhIfoaRbUtyr6QrZEfzz+4AD9TVudXsjh2/r92g8vqQ4lQDxyYcWP5Ubv2mtUJHXrde6XaMre4RU/nzfDzWeBMkjACjwtJD9JXq3R7eskMsdOofEqe7HbfaAy8na2pEiEHLwFzvEHORKMvBOITEAEmSOdrEHbZgpQKnIXLxmEiVwIPe2lfGPJlcpHfSRHlIu0op4Q3EWtDzk3YQ9nF44zha4gKpydG7nMoQZHlJwoDg6KsVKEkqUah6s3buhk4VpDXFT5T6zq/DosJPyETJyUob30JIUYZfXSdbHzayDlB11bXlTK0jWmbz1Ca9Sa8Le8b4b7cMXmARvqB9Hs8UyfBtuZPSub4/MTGpJpeAG0RAMe/sFy4f/xqaeTiYnWdTOCw1BRjXarrECBcghV6MQADpe7fGk/LZuzXL16o01bwQKn/K5QG2wbgFPyVzlzmKD78cKeYvxugsRKAeZPPVlniwNvZzWfh2E1daUQUOezNAAINAgsqYfdEZ+5XVgpMDBRlM90P4CzT3dHIvAgR1A0i4DzgB+lYDoQoKchH8BiN7GrmpR/5+CW4BID40+WAIwFaRkM8dApKTDsYBhmtMV/sgxqrQcDXZNwqBk6mCCk3KpgRkzfg2Tc3gQOI0dPBzNJp4BCq64BkbVMJVv8gLcIAUeBIUvQeAd4e/6c6Hda07eStRkMjd/pjcOKsHDw4qiGWcxIrcVJuH3JaSvGaYBQfgrg3BObrlWqlcMgi614WOqhX+P2EDnJQ2qU049vEcQjQfjj3H9Cd9MkD9cJDpXkViuvkMtMj43ehe6nRmOVJtgPVO273ewe5G36BAm/mcxqUbjlDn7d2Ruu20BRc5cfZ1JBra/D55XpildAdIeezHoL5ljIcTAY4TqTdIQTxmn23/m+D2dOeUa7jN0+4tJEzNUEE+KwyYKr1i/8zvOy1H+HjpCo851E+JuiBA/IEtgjbNfWZbdr2RPpRuy26AaY0fDhkV2IcvqEta2jIT5kZOe9B/gBmQJEpl8cScMulaOO5H52IVMWmosf2RDprSb8p3i9QBvINwWL6i6Yltd5SVjr3RR+4U70Mwwp0ju6u/gsvdoFceGZxMk8Rs4LlPhLealwY3eNWi32heyemES1mGMKujWt7HivN9YbrlbLVU+ItuCQIajk2is33bTvSRekrrQe5BYFOZACeXOTMEM0hFITkXfYDcKQehAoLXrkcaz83fYCIEQnFgv4GZ9wjiuF5SU6YOA5npYqZFCmcPaMUHRReWiP2EZX2BMu6PQUkC37/7t5FOlN0/lOMyye6ZJAnEYtfsJZedP0sGW4NSV+fx9qMAR6uw04rA3enf7FbaCWUdDWPsx/1OQbt2OAbNldckLpfQJ9B5DnA941wCDx6DWUGLcd6wLxvAbRFQ9uBRoaI7gpnVJmnQu2SL7CNUY8GOFiSCzl2px2/mtvf5sNVXis2sH7gBSzDB83+ltUCfwSL9U55nHB4P8/aOeg5xO+7z7rwWLbPPo55TRxAJLBlAhKkQzc52UlMDNVXvIJBfMlc6KBYPZRioDhKF2YRanUdLraiUQuyDH4PS9RlX7+mDPht4nazxhhjB8Om/j6b9TqOb9DiTaQYSKVpBHKxQYBDhpKIP7pCYL4y7SOf54UoDADGSbW/NEj4ivUcEYIKNCaVMKzuDc74d0e4XMHz/WIUAXCZB/WRIw93EkNL9LtfCquZ1j4TAQMnklwLkvQNSk8iuZua6bwEEVvXd155S6a1n45fSD8gVBlobQHALQx4RBWDWNgGBi5aHGN0hHh0ZpUyPm3V/gIgABRXv/OaM5UTKjZcfeo4QZAKv8591DZXf6boY8fqukrHOit7p9XIHxIQS+tNKPr+TJ3wgOJCpKQjUj07zQUGHbMByMDAjG6mczJvCbjLZiDHXkIP68mYIM4HNInA17oYE0pAjQyFzx57H7KaVNtS3PHsOAYq72R/a4e4RX5kmCC4nYrdwplcvBo8hvsjkdbxZ55ZQsH8g3bLHT5Qp6Ee2iHeJr6DIH5XMhWrC/e3HfIxQSwS8BpGezbnBZrC2ZEPDyyCw5DcwpbzIvrsCnUuMHiITNoltIEnSPAVDcN+fYr+UmIRv6kXrrMy741CjEYsXBy8MslpROh91coyyXZnlhSAz0z0aOUeNjo=", + "hash": "16086804604944857114083318777569758960347127563430581910767940324520536993210" + } + }, "diverse": { "digest": "e5400cb7967d5a1f41e41b0ea0b5ef59b90591fc9e22149e2ce71de32261326", "methods": { diff --git a/tests/vk-regression/vk-regression.ts b/tests/vk-regression/vk-regression.ts index fe2c92140..24a7370b5 100644 --- a/tests/vk-regression/vk-regression.ts +++ b/tests/vk-regression/vk-regression.ts @@ -8,6 +8,7 @@ import { keccakAndEcdsa, } from '../../src/examples/crypto/ecdsa/ecdsa.js'; import { SHA256Program } from '../../src/examples/crypto/sha256/sha256.js'; +import { BLAKE2BProgram } from '../../src/examples/crypto/blake2b/blake2b.js' import { GroupCS, BitwiseCS, @@ -61,6 +62,7 @@ const ConstraintSystems: MinimumConstraintSystem[] = [ ecdsa, keccakAndEcdsa, SHA256Program, + BLAKE2BProgram, diverse, ];