From d3578532c9b6886c5b78f08ffefa340a04cb7f1d Mon Sep 17 00:00:00 2001 From: boray Date: Thu, 18 Jul 2024 23:41:44 +0300 Subject: [PATCH 01/38] add blake2b --- src/examples/crypto/blake2b/blake2b.ts | 18 ++ src/examples/crypto/blake2b/run.ts | 23 +++ src/lib/provable/gadgets/arithmetic.ts | 52 +++++- src/lib/provable/gadgets/bitwise.ts | 33 ++++ src/lib/provable/gadgets/blake2b.ts | 186 +++++++++++++++++++++ src/lib/provable/gadgets/gadgets.ts | 17 +- src/lib/provable/int.ts | 30 +++- src/lib/provable/test/blake2b.unit-test.ts | 51 ++++++ 8 files changed, 402 insertions(+), 8 deletions(-) create mode 100644 src/examples/crypto/blake2b/blake2b.ts create mode 100644 src/examples/crypto/blake2b/run.ts create mode 100644 src/lib/provable/gadgets/blake2b.ts create mode 100644 src/lib/provable/test/blake2b.unit-test.ts diff --git a/src/examples/crypto/blake2b/blake2b.ts b/src/examples/crypto/blake2b/blake2b.ts new file mode 100644 index 000000000..032810262 --- /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(64).provable, + methods: { + blake2b: { + privateInputs: [Bytes12.provable], + async method(xs: Bytes12) { + return Gadgets.BLAKE2B.hash(xs); + }, + }, + }, +}); diff --git a/src/examples/crypto/blake2b/run.ts b/src/examples/crypto/blake2b/run.ts new file mode 100644 index 000000000..c6386b799 --- /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() !== + 'fa02d55d26bc5cda1e2d67fb7424f6132c58fed81a52816342795de54d3b2d8b91749f267d2491ed05ca0cbbd0e641cc1758b92e99eb1d8771060ebacbc83c25' +) + throw new Error('Invalid blake2b digest!'); +if (!isValid) throw new Error('Invalid proof'); diff --git a/src/lib/provable/gadgets/arithmetic.ts b/src/lib/provable/gadgets/arithmetic.ts index 3f49fb610..88f465692 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, quotientBits = 32) { if (n.isConstant()) { @@ -51,3 +51,51 @@ function divMod32(n: Field, quotientBits = 32) { function addMod32(x: Field, y: Field) { return divMod32(x.add(y), 1).remainder; } + + +function divMod64(n: Field, quotientBits = 64) { + if (n.isConstant()) { + /* + assert( + n.toBigInt() < 1n << 64n, + `n needs to fit into 64 bit, 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); + // why do we have to do this? + return [q, r] satisfies [bigint, bigint]; + } + ); + + if (quotientBits === 1) { + quotient.assertBool(); + } 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), 1).remainder; +} \ No newline at end of file diff --git a/src/lib/provable/gadgets/bitwise.ts b/src/lib/provable/gadgets/bitwise.ts index b1b3a025e..7d845126c 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, @@ -185,6 +186,38 @@ function and(a: Field, b: Field, length: number) { return outputAnd; } +function or(a: Field, b: Field, length: number) { + // check that both input lengths are positive + assert(length > 0, `Input lengths need to be positive values.`); + + // check that length does not exceed maximum field size in bits + assert( + length <= Field.sizeInBits, + `Length ${length} exceeds maximum of ${Field.sizeInBits} bits.` + ); + + // obtain pad length until the length is a multiple of 16 for n-bit length lookup table + let padLength = Math.ceil(length / 16) * 16; + + // handle constant case + if (a.isConstant() && b.isConstant()) { + let max = 1n << BigInt(padLength); + + assert(a.toBigInt() < max, `${a} does not fit into ${padLength} bits`); + assert(b.toBigInt() < max, `${b} does not fit into ${padLength} bits`); + + return new Field(a.toBigInt() | b.toBigInt()); + } + + // calculate expect and output + let outputOr = Provable.witness(Field, () => a.toBigInt() | b.toBigInt()); + + outputOr.assertEquals( + not(and(not(a, length), not(b, length), length), length) + ); + return outputOr; +} + 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..5f36a2016 --- /dev/null +++ b/src/lib/provable/gadgets/blake2b.ts @@ -0,0 +1,186 @@ +// https://www.blake2.net/blake2.pdf +import { UInt64, UInt8 } from '../int.js'; +import { FlexibleBytes } from '../bytes.js'; +import { Bytes } from '../wrapped-classes.js'; + + +export { BLAKE2B }; + +const IV: UInt64[] = [ + UInt64.from(0x6a09e667f3bcc908n), + UInt64.from(0xbb67ae8584caa73bn), + UInt64.from(0x3c6ef372fe94f82bn), + UInt64.from(0xa54ff53a5f1d36f1n), + UInt64.from(0x510e527fade682d1n), + UInt64.from(0x9b05688c2b3e6c1fn), + UInt64.from(0x1f83d9abfb41bd6bn), + UInt64.from(0x5be0cd19137e2179n), +]; + +const SIGMA: number[][] = [ + [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) { + let input = Bytes.from(data).bytes; + const state = initialize(64); + update(state, input); + let out = final(state); + return Bytes.from(out); + } +}; + +function initialize(outlen: number): { + h: UInt64[]; + t: UInt64[]; + f: UInt64[]; + buf: UInt8[]; + buflen: number; + outlen: number; +} { + const h = IV.slice(); // shallow copy IV to h + h[0] = UInt64.from(0x01010000).xor(UInt64.from(outlen)).xor(h[0]); + return { + h, + t: [UInt64.zero, UInt64.zero], + f: [UInt64.zero, UInt64.zero], + buf: [], + buflen: 0, + outlen, + }; +} + +function Mix( + v: UInt64[], + a: number, + b: number, + c: number, + d: number, + x: UInt64, + y: UInt64 +) { + v[a] = v[a].addMod64(v[b].addMod64(x)); + v[d] = v[d].xor(v[a]).rotate(32, 'right'); + + v[c] = v[c].addMod64(v[d]); + v[b] = v[b].xor(v[c]).rotate(24, 'right'); + + v[a] = v[a].addMod64(v[b].addMod64(y)); + v[d] = v[d].xor(v[a]).rotate(16, 'right'); + + v[c] = v[c].addMod64(v[d]); + v[b] = v[b].xor(v[c]).rotate(63, 'right'); +} + +function compress(state: { + h: UInt64[]; + t: UInt64[]; + f: UInt64[]; + buf: UInt8[]; + buflen: number; + outlen: number; +}): void { + const { h, t, f, buf } = state; + const v = h.concat(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 + v[14] = v[14].xor(f[0]); + v[15] = v[15].xor(f[1]); + + const m: UInt64[] = []; + for (let i = 0; i < 16; i++) { + m.push( + UInt64.from( + buf[i * 8] + .toUInt64() + .or(buf[i * 8 + 1].toUInt64().leftShift(8)) + .or(buf[i * 8 + 2].toUInt64().leftShift(16)) + .or(buf[i * 8 + 3].toUInt64().leftShift(24)) + .or(buf[i * 8 + 4].toUInt64().leftShift(32)) + .or(buf[i * 8 + 5].toUInt64().leftShift(40)) + .or(buf[i * 8 + 6].toUInt64().leftShift(48)) + .or(buf[i * 8 + 7].toUInt64().leftShift(56)) + ) + ); +} + + for (let i = 0; i < 12; i++) { + const s = SIGMA[i % 10]; + Mix(v, 0, 4, 8, 12, m[s[0]], m[s[1]]); + Mix(v, 1, 5, 9, 13, m[s[2]], m[s[3]]); + Mix(v, 2, 6, 10, 14, m[s[4]], m[s[5]]); + Mix(v, 3, 7, 11, 15, m[s[6]], m[s[7]]); + + Mix(v, 0, 5, 10, 15, m[s[8]], m[s[9]]); + Mix(v, 1, 6, 11, 12, m[s[10]], m[s[11]]); + Mix(v, 2, 7, 8, 13, m[s[12]], m[s[13]]); + Mix(v, 3, 4, 9, 14, m[s[14]], m[s[15]]); + } + + for (let i = 0; i < 8; i++) { + h[i] = v[i].xor(v[i + 8]).xor(h[i]); + } +} + +function update( + state: { + h: UInt64[]; + t: UInt64[]; + f: UInt64[]; + buf: UInt8[]; + buflen: number; + outlen: number; + }, + input: UInt8[] +): void { + for (let i = 0; i < input.length; i++) { + if (state.buflen === 128) { + state.t[0] = state.t[0].add(128); // ? + if (state.t[0].equals(UInt64.zero)) { + state.t[1] = state.t[1].addMod64(UInt64.one); + } + compress(state); + state.buflen = 0; + } + state.buf[state.buflen++] = input[i]; + } +} + +function final(state: { + h: UInt64[]; + t: UInt64[]; + f: UInt64[]; + buf: UInt8[]; + buflen: number; + outlen: number; +}): UInt8[] { + state.t[0] = state.t[0].add(state.buflen); // ? + if (state.t[0].equals(UInt64.zero)) { + state.t[1] = state.t[1].add(UInt64.zero); +} + state.f[0] = UInt64.from('0xFFFFFFFFFFFFFFFF'); + + while (state.buflen < 128) { + state.buf[state.buflen++] = UInt8.from(0); + } + compress(state); + + 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 c28c3d8ce..b2cb80be6 100644 --- a/src/lib/provable/gadgets/gadgets.ts +++ b/src/lib/provable/gadgets/gadgets.ts @@ -27,8 +27,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 }; @@ -837,8 +838,14 @@ const Gadgets = { */ divMod32, - /** - * Addition modulo 2^32. The operation adds two {@link Field} elements in the range [0, 2^64] and returns the result modulo 2^32. + + addMod32, + + + divMod64, + + /** + * Addition modulo 2^64. The operation adds two {@link Field} elements in the range [0, 2^64] and returns the result modulo 2^32. * * Asserts that the result is in the range [0, 2^32) using {@link Gadgets.rangeCheck32}. * @@ -854,8 +861,7 @@ const Gadgets = { * Gadgets.addMod32(a, b).assertEquals(Field(8n)); * ``` * */ - addMod32, - + addMod64, /** * Implementation of the [SHA256 hash function.](https://en.wikipedia.org/wiki/SHA-2) Hash function with 256bit output. * @@ -875,4 +881,5 @@ const Gadgets = { * */ SHA256: SHA256, + BLAKE2B: BLAKE2B }; diff --git a/src/lib/provable/int.ts b/src/lib/provable/int.ts index bc4e741f9..4818f4f9e 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,14 @@ class UInt64 extends CircuitValue { return new UInt64(Bitwise.and(this.value, x.value, UInt64.NUM_BITS).value); } + /** + * + */ + or(x: UInt64) { + //return this.xor(x).xor(this.and(x)); + 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. */ @@ -1442,6 +1457,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/blake2b.unit-test.ts b/src/lib/provable/test/blake2b.unit-test.ts new file mode 100644 index 000000000..55dcaade9 --- /dev/null +++ b/src/lib/provable/test/blake2b.unit-test.ts @@ -0,0 +1,51 @@ +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'; + + +const Blake2bProgram = ZkProgram({ + name: `blake2b`, + publicOutput: Bytes(64).provable, + methods: { + blake2b: { + privateInputs: [Bytes(43).provable], + async method(preImage: Bytes) { + return Gadgets.BLAKE2B.hash(preImage); + }, + }, + }, +}); + +await Blake2bProgram.compile(); + +for (let { preimage, hash } of testVectors()) { + let actual = Gadgets.BLAKE2B.hash(Bytes.fromString(preimage)); + expect(actual.toHex()).toEqual(hash); +} + + +function testVectors() { + return [ + { + preimage: 'The quick brown fox jumps over the lazy dog', + hash: 'a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918' + }, + { + preimage: 'o1js', + hash: 'abe5652d5c204163a4b418b33577b8ccebd5a5ed3d8cbb9781e7ea4a1bc3344c9e5e5707112d656f642927a42d34f96439f68a1ff0a2aa621a3f1fbb2521b18f', + }, + { + preimage: '', + hash: '786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce' + } + + ]; +} From cb93722d43222317ca63c5b9851a1107fc35fd7b Mon Sep 17 00:00:00 2001 From: boray Date: Mon, 22 Jul 2024 15:53:48 +0300 Subject: [PATCH 02/38] add or gadget and improvements --- src/examples/crypto/blake2b/blake2b.ts | 4 +- src/examples/crypto/blake2b/run.ts | 10 +-- src/lib/provable/crypto/hash.ts | 7 ++ src/lib/provable/gadgets/blake2b.ts | 97 +++++++++++----------- src/lib/provable/gadgets/gadgets.ts | 6 ++ src/lib/provable/test/blake2b.unit-test.ts | 53 +++++++++++- 6 files changed, 120 insertions(+), 57 deletions(-) diff --git a/src/examples/crypto/blake2b/blake2b.ts b/src/examples/crypto/blake2b/blake2b.ts index 032810262..d34caaee5 100644 --- a/src/examples/crypto/blake2b/blake2b.ts +++ b/src/examples/crypto/blake2b/blake2b.ts @@ -1,10 +1,10 @@ import { Bytes, Gadgets, ZkProgram } from 'o1js'; -export { Blake2bProgram, Bytes12 }; +export { BLAKE2BProgram, Bytes12 }; class Bytes12 extends Bytes(12) {} -let Blake2bProgram = ZkProgram({ +let BLAKE2BProgram = ZkProgram({ name: 'blake2b', publicOutput: Bytes(64).provable, methods: { diff --git a/src/examples/crypto/blake2b/run.ts b/src/examples/crypto/blake2b/run.ts index c6386b799..43568c28d 100644 --- a/src/examples/crypto/blake2b/run.ts +++ b/src/examples/crypto/blake2b/run.ts @@ -1,17 +1,17 @@ -import { Bytes12, Blake2bProgram } from './blake2b.js'; +import { Bytes12, BLAKE2BProgram } from './blake2b.js'; console.time('compile'); -await Blake2bProgram.compile(); +await BLAKE2BProgram.compile(); console.timeEnd('compile'); let preimage = Bytes12.fromString('hello world!'); -console.log('blake2b rows:', (await Blake2bProgram.analyzeMethods()).blake2b.rows); +console.log('blake2b rows:', (await BLAKE2BProgram.analyzeMethods()).blake2b.rows); console.time('prove'); -let proof = await Blake2bProgram.blake2b(preimage); +let proof = await BLAKE2BProgram.blake2b(preimage); console.timeEnd('prove'); -let isValid = await Blake2bProgram.verify(proof); +let isValid = await BLAKE2BProgram.verify(proof); console.log('digest:', proof.publicOutput.toHex()); diff --git a/src/lib/provable/crypto/hash.ts b/src/lib/provable/crypto/hash.ts index 3c7edaf81..1f16e1ed9 100644 --- a/src/lib/provable/crypto/hash.ts +++ b/src/lib/provable/crypto/hash.ts @@ -2,6 +2,7 @@ import { Gadgets } from '../gadgets/gadgets.js'; import { Poseidon } from './poseidon.js'; import { Keccak } from './keccak.js'; import { Bytes } from '../wrapped-classes.js'; +import { BLAKE2 } from 'node_modules/@noble/hashes/_blake2.js'; export { Hash }; @@ -138,4 +139,10 @@ const Hash = { return Keccak.preNist(512, bytes); }, }, + + BLAKE2B: { + hash(bytes: Bytes) { + return Gadgets.BLAKE2B.hash(bytes); + }, + } }; diff --git a/src/lib/provable/gadgets/blake2b.ts b/src/lib/provable/gadgets/blake2b.ts index 5f36a2016..77992ca38 100644 --- a/src/lib/provable/gadgets/blake2b.ts +++ b/src/lib/provable/gadgets/blake2b.ts @@ -3,41 +3,44 @@ import { UInt64, UInt8 } from '../int.js'; import { FlexibleBytes } from '../bytes.js'; import { Bytes } from '../wrapped-classes.js'; - export { BLAKE2B }; -const IV: UInt64[] = [ - UInt64.from(0x6a09e667f3bcc908n), - UInt64.from(0xbb67ae8584caa73bn), - UInt64.from(0x3c6ef372fe94f82bn), - UInt64.from(0xa54ff53a5f1d36f1n), - UInt64.from(0x510e527fade682d1n), - UInt64.from(0x9b05688c2b3e6c1fn), - UInt64.from(0x1f83d9abfb41bd6bn), - UInt64.from(0x5be0cd19137e2179n), -]; - -const SIGMA: number[][] = [ - [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 BLAKE2BConstants = { + IV: [ + 0x6a09e667f3bcc908n, + 0xbb67ae8584caa73bn, + 0x3c6ef372fe94f82bn, + 0xa54ff53a5f1d36f1n, + 0x510e527fade682d1n, + 0x9b05688c2b3e6c1fn, + 0x1f83d9abfb41bd6bn, + 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) { - let input = Bytes.from(data).bytes; - const state = initialize(64); - update(state, input); - let out = final(state); + hash(data: FlexibleBytes, digest_length = 64) { + const state = initialize(digest_length); + update(state, Bytes.from(data).bytes); + const out = final(state); return Bytes.from(out); - } + }, + get IV() { + return BLAKE2BConstants.IV.map((x) => UInt64.from(x)); + }, }; function initialize(outlen: number): { @@ -48,7 +51,7 @@ function initialize(outlen: number): { buflen: number; outlen: number; } { - const h = IV.slice(); // shallow copy IV to h + const h = BLAKE2B.IV.slice(); // shallow copy IV to h h[0] = UInt64.from(0x01010000).xor(UInt64.from(outlen)).xor(h[0]); return { h, @@ -60,7 +63,7 @@ function initialize(outlen: number): { }; } -function Mix( +function G( v: UInt64[], a: number, b: number, @@ -77,7 +80,7 @@ function Mix( v[a] = v[a].addMod64(v[b].addMod64(y)); v[d] = v[d].xor(v[a]).rotate(16, 'right'); - + v[c] = v[c].addMod64(v[d]); v[b] = v[b].xor(v[c]).rotate(63, 'right'); } @@ -91,7 +94,7 @@ function compress(state: { outlen: number; }): void { const { h, t, f, buf } = state; - const v = h.concat(IV); // initalize local work vector. First half from state and second half from IV. + 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 @@ -112,20 +115,20 @@ function compress(state: { .or(buf[i * 8 + 6].toUInt64().leftShift(48)) .or(buf[i * 8 + 7].toUInt64().leftShift(56)) ) - ); -} + ); + } for (let i = 0; i < 12; i++) { - const s = SIGMA[i % 10]; - Mix(v, 0, 4, 8, 12, m[s[0]], m[s[1]]); - Mix(v, 1, 5, 9, 13, m[s[2]], m[s[3]]); - Mix(v, 2, 6, 10, 14, m[s[4]], m[s[5]]); - Mix(v, 3, 7, 11, 15, m[s[6]], m[s[7]]); - - Mix(v, 0, 5, 10, 15, m[s[8]], m[s[9]]); - Mix(v, 1, 6, 11, 12, m[s[10]], m[s[11]]); - Mix(v, 2, 7, 8, 13, m[s[12]], m[s[13]]); - Mix(v, 3, 4, 9, 14, m[s[14]], m[s[15]]); + 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++) { @@ -168,7 +171,7 @@ function final(state: { state.t[0] = state.t[0].add(state.buflen); // ? if (state.t[0].equals(UInt64.zero)) { state.t[1] = state.t[1].add(UInt64.zero); -} + } state.f[0] = UInt64.from('0xFFFFFFFFFFFFFFFF'); while (state.buflen < 128) { diff --git a/src/lib/provable/gadgets/gadgets.ts b/src/lib/provable/gadgets/gadgets.ts index b2cb80be6..0aeda514e 100644 --- a/src/lib/provable/gadgets/gadgets.ts +++ b/src/lib/provable/gadgets/gadgets.ts @@ -17,6 +17,7 @@ import { rotate64, xor, and, + or, leftShift64, rightShift64, leftShift32, @@ -450,6 +451,11 @@ const Gadgets = { return and(a, b, length); }, + + or(a: Field, b: Field, length: number) { + return or(a, b, length); + }, + /** * Multi-range check. * diff --git a/src/lib/provable/test/blake2b.unit-test.ts b/src/lib/provable/test/blake2b.unit-test.ts index 55dcaade9..9e516dc01 100644 --- a/src/lib/provable/test/blake2b.unit-test.ts +++ b/src/lib/provable/test/blake2b.unit-test.ts @@ -26,8 +26,8 @@ const Blake2bProgram = ZkProgram({ await Blake2bProgram.compile(); -for (let { preimage, hash } of testVectors()) { - let actual = Gadgets.BLAKE2B.hash(Bytes.fromString(preimage)); +for (let { digest_length ,preimage, hash } of testVectors()) { + let actual = Gadgets.BLAKE2B.hash(Bytes.fromString(preimage), digest_length); expect(actual.toHex()).toEqual(hash); } @@ -35,17 +35,64 @@ for (let { preimage, hash } of testVectors()) { 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: 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' } - ]; } From 28463964ca6190041058ba124888ccd16de7628b Mon Sep 17 00:00:00 2001 From: boray Date: Wed, 24 Jul 2024 02:00:16 +0300 Subject: [PATCH 03/38] add comments and tests --- src/examples/crypto/blake2b/blake2b.ts | 6 ++--- src/examples/crypto/blake2b/run.ts | 2 +- src/lib/provable/gadgets/arithmetic.ts | 7 ------ src/lib/provable/gadgets/bitwise.ts | 13 ++++------ src/lib/provable/gadgets/gadgets.ts | 21 ++++++++++++++++ src/lib/provable/int.ts | 13 ++++++++-- src/lib/provable/test/bitwise.unit-test.ts | 28 ++++++++++++++++++++++ src/lib/provable/test/blake2b.unit-test.ts | 14 ----------- 8 files changed, 69 insertions(+), 35 deletions(-) diff --git a/src/examples/crypto/blake2b/blake2b.ts b/src/examples/crypto/blake2b/blake2b.ts index d34caaee5..24f4c5816 100644 --- a/src/examples/crypto/blake2b/blake2b.ts +++ b/src/examples/crypto/blake2b/blake2b.ts @@ -6,12 +6,12 @@ class Bytes12 extends Bytes(12) {} let BLAKE2BProgram = ZkProgram({ name: 'blake2b', - publicOutput: Bytes(64).provable, + publicOutput: Bytes(32), methods: { blake2b: { - privateInputs: [Bytes12.provable], + privateInputs: [Bytes12], async method(xs: Bytes12) { - return Gadgets.BLAKE2B.hash(xs); + return Gadgets.BLAKE2B.hash(xs,32); }, }, }, diff --git a/src/examples/crypto/blake2b/run.ts b/src/examples/crypto/blake2b/run.ts index 43568c28d..2ce6c834d 100644 --- a/src/examples/crypto/blake2b/run.ts +++ b/src/examples/crypto/blake2b/run.ts @@ -17,7 +17,7 @@ console.log('digest:', proof.publicOutput.toHex()); if ( proof.publicOutput.toHex() !== - 'fa02d55d26bc5cda1e2d67fb7424f6132c58fed81a52816342795de54d3b2d8b91749f267d2491ed05ca0cbbd0e641cc1758b92e99eb1d8771060ebacbc83c25' + '4fccfb4d98d069558aa93e9565f997d81c33b080364efd586e77a433ddffc5e2' ) throw new Error('Invalid blake2b digest!'); if (!isValid) throw new Error('Invalid proof'); diff --git a/src/lib/provable/gadgets/arithmetic.ts b/src/lib/provable/gadgets/arithmetic.ts index 88f465692..901a8e3de 100644 --- a/src/lib/provable/gadgets/arithmetic.ts +++ b/src/lib/provable/gadgets/arithmetic.ts @@ -55,12 +55,6 @@ function addMod32(x: Field, y: Field) { function divMod64(n: Field, quotientBits = 64) { if (n.isConstant()) { - /* - assert( - n.toBigInt() < 1n << 64n, - `n needs to fit into 64 bit, but got ${n.toBigInt()}` - ); */ - let nBigInt = n.toBigInt(); let q = nBigInt >> 64n; let r = nBigInt - (q << 64n); @@ -76,7 +70,6 @@ function divMod64(n: Field, quotientBits = 64) { let nBigInt = n.toBigInt(); let q = nBigInt >> 64n; let r = nBigInt - (q << 64n); - // why do we have to do this? return [q, r] satisfies [bigint, bigint]; } ); diff --git a/src/lib/provable/gadgets/bitwise.ts b/src/lib/provable/gadgets/bitwise.ts index 63bd10e23..180e8abbd 100644 --- a/src/lib/provable/gadgets/bitwise.ts +++ b/src/lib/provable/gadgets/bitwise.ts @@ -175,14 +175,9 @@ function and(a: Field, b: Field, length: number) { } function or(a: Field, b: Field, length: number) { - // check that both input lengths are positive - assert(length > 0, `Input lengths need to be positive values.`); - - // check that length does not exceed maximum field size in bits - assert( - length <= Field.sizeInBits, - `Length ${length} exceeds maximum of ${Field.sizeInBits} bits.` - ); + // Validate at 240 bits to ensure padLength (next multiple of 16) doesn't exceed 254 bits, + // preventing potential underconstraint issues in the circuit + validateBitLength(length, 240, 'not'); // obtain pad length until the length is a multiple of 16 for n-bit length lookup table let padLength = Math.ceil(length / 16) * 16; @@ -203,6 +198,8 @@ function or(a: Field, b: Field, length: number) { outputOr.assertEquals( not(and(not(a, length), not(b, length), length), length) ); + + // return the result of the or operation return outputOr; } diff --git a/src/lib/provable/gadgets/gadgets.ts b/src/lib/provable/gadgets/gadgets.ts index 1b75900a5..4cb3252c7 100644 --- a/src/lib/provable/gadgets/gadgets.ts +++ b/src/lib/provable/gadgets/gadgets.ts @@ -452,6 +452,27 @@ const Gadgets = { }, +/** + * 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(b); // ... 000111 + * c.assertEquals(7); + * ``` + */ or(a: Field, b: Field, length: number) { return or(a, b, length); }, diff --git a/src/lib/provable/int.ts b/src/lib/provable/int.ts index 4818f4f9e..7cbf3d381 100644 --- a/src/lib/provable/int.ts +++ b/src/lib/provable/int.ts @@ -410,11 +410,20 @@ 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 this.xor(x).xor(this.and(x)); return new UInt64(Bitwise.or(this.value, x.value, UInt64.NUM_BITS).value); } diff --git a/src/lib/provable/test/bitwise.unit-test.ts b/src/lib/provable/test/bitwise.unit-test.ts index 23b3c38bc..3a2f107c5 100644 --- a/src/lib/provable/test/bitwise.unit-test.ts +++ b/src/lib/provable/test/bitwise.unit-test.ts @@ -57,6 +57,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 +107,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), @@ -183,6 +193,18 @@ await equivalentAsync({ from: [maybeField, maybeField], to: field }, { runs })( } ); +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.or(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'); @@ -267,6 +289,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 index 9e516dc01..d94b8bf96 100644 --- a/src/lib/provable/test/blake2b.unit-test.ts +++ b/src/lib/provable/test/blake2b.unit-test.ts @@ -11,20 +11,6 @@ import { Random, sample } from '../../testing/random.js'; import { expect } from 'expect'; -const Blake2bProgram = ZkProgram({ - name: `blake2b`, - publicOutput: Bytes(64).provable, - methods: { - blake2b: { - privateInputs: [Bytes(43).provable], - async method(preImage: Bytes) { - return Gadgets.BLAKE2B.hash(preImage); - }, - }, - }, -}); - -await Blake2bProgram.compile(); for (let { digest_length ,preimage, hash } of testVectors()) { let actual = Gadgets.BLAKE2B.hash(Bytes.fromString(preimage), digest_length); From 9dd79aa2c0f9a056d3cefeef08ed91085e2b8a1e Mon Sep 17 00:00:00 2001 From: boray Date: Wed, 24 Jul 2024 04:17:14 +0300 Subject: [PATCH 04/38] fix bitwise test --- src/lib/provable/test/bitwise.unit-test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/provable/test/bitwise.unit-test.ts b/src/lib/provable/test/bitwise.unit-test.ts index 3a2f107c5..74e504af1 100644 --- a/src/lib/provable/test/bitwise.unit-test.ts +++ b/src/lib/provable/test/bitwise.unit-test.ts @@ -108,7 +108,7 @@ await Bitwise.compile(); (x, y) => Gadgets.and(x, y, length) ); equivalent({ from: [uint(length), uint(length)], to: field })( - (x, y) => x & y, + (x, y) => x | y, (x, y) => Gadgets.or(x, y, length) ); // NOT unchecked From 24c743a6db3478e0bbbee3b9ea9333a710252636 Mon Sep 17 00:00:00 2001 From: boray Date: Wed, 24 Jul 2024 17:34:48 +0300 Subject: [PATCH 05/38] add comments to gadgets --- src/lib/provable/gadgets/gadgets.ts | 40 +++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/lib/provable/gadgets/gadgets.ts b/src/lib/provable/gadgets/gadgets.ts index 4cb3252c7..96a7f2328 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, @@ -865,14 +865,8 @@ const Gadgets = { */ divMod32, - - addMod32, - - - divMod64, - - /** - * Addition modulo 2^64. The operation adds two {@link Field} elements in the range [0, 2^64] and returns the result modulo 2^32. + /** + * Addition modulo 2^32. The operation adds two {@link Field} elements in the range [0, 2^64] and returns the result modulo 2^32. * * Asserts that the result is in the range [0, 2^32) using {@link Gadgets.rangeCheck32}. * @@ -888,7 +882,14 @@ const Gadgets = { * Gadgets.addMod32(a, b).assertEquals(Field(8n)); * ``` * */ + addMod32, + + + divMod64, + + addMod64, + /** * Implementation of the [SHA256 hash function.](https://en.wikipedia.org/wiki/SHA-2) Hash function with 256bit output. * @@ -908,5 +909,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 bit 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 }; From 044cbe1e47e0211d6d8fd8a0a7dd0b8b0904109b Mon Sep 17 00:00:00 2001 From: boray Date: Wed, 24 Jul 2024 17:35:01 +0300 Subject: [PATCH 06/38] dump vks --- .../vk-regression/plain-constraint-system.ts | 13 ++++++++++++ tests/vk-regression/vk-regression.json | 21 +++++++++++++++++++ tests/vk-regression/vk-regression.ts | 2 ++ 3 files changed, 36 insertions(+) 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 6dbad49ac..fd090840d 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": 5072, + "digest": "368d3b3888b60bb3676b855e8b328dc9" } }, "verificationKey": { @@ -300,6 +308,19 @@ "hash": "14970780915659850828924502346680433225700971894695273700497112528587195815959" } }, + "blake2b": { + "digest": "1203b1ff47e8fee2703a9857ef079b9264fe4bd7c263c1db5079e722203d3c4", + "methods": { + "blake2b": { + "rows": 4479, + "digest": "cb05769ba27e28cfa8ef3c04c043cf81" + } + }, + "verificationKey": { + "data": "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", + "hash": "23122100856240843309657022506980859401916791014549689915478926277345430629799" + } + }, "diverse": { "digest": "e00864ae3864d3fc7ae27c5b68305a371d54671bec3014c47d1ffb1d19863b7", "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, ]; From 81bc5a983f199bdd7d19b13ddbffac4df63d1976 Mon Sep 17 00:00:00 2001 From: boray Date: Fri, 26 Jul 2024 03:25:57 +0300 Subject: [PATCH 07/38] add doccoments and test --- src/lib/provable/gadgets/gadgets.ts | 44 +++++++++++++++---- src/lib/provable/test/arithmetic.unit-test.ts | 44 +++++++++++++++++-- 2 files changed, 77 insertions(+), 11 deletions(-) diff --git a/src/lib/provable/gadgets/gadgets.ts b/src/lib/provable/gadgets/gadgets.ts index 96a7f2328..5eeb2a547 100644 --- a/src/lib/provable/gadgets/gadgets.ts +++ b/src/lib/provable/gadgets/gadgets.ts @@ -450,13 +450,10 @@ 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. @@ -476,7 +473,7 @@ const Gadgets = { or(a: Field, b: Field, length: number) { return or(a, b, length); }, - + /** * Multi-range check. * @@ -884,10 +881,41 @@ 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, /** @@ -928,5 +956,5 @@ const Gadgets = { * ``` * */ - BLAKE2B: BLAKE2B + BLAKE2B: BLAKE2B, }; 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; + } +); From 58a07a0d04373c3afe3b7b38b0021ad41e1bf0a5 Mon Sep 17 00:00:00 2001 From: boray Date: Fri, 26 Jul 2024 15:45:45 +0300 Subject: [PATCH 08/38] add yoni's fix #1763 --- src/lib/provable/gadgets/arithmetic.ts | 22 ++++++++++++++++++---- src/lib/provable/gadgets/sha256.ts | 6 +++--- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/lib/provable/gadgets/arithmetic.ts b/src/lib/provable/gadgets/arithmetic.ts index 901a8e3de..250a48d62 100644 --- a/src/lib/provable/gadgets/arithmetic.ts +++ b/src/lib/provable/gadgets/arithmetic.ts @@ -6,7 +6,12 @@ import { rangeCheck32, rangeCheck64, rangeCheckN } from './range-check.js'; export { divMod32, addMod32, divMod64, addMod64 }; -function divMod32(n: Field, quotientBits = 32) { +function divMod32(n: Field, nBits = 64) { + 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 - 32); + if (n.isConstant()) { assert( n.toBigInt() < 1n << 64n, @@ -49,12 +54,21 @@ function divMod32(n: Field, quotientBits = 32) { } function addMod32(x: Field, y: Field) { - return divMod32(x.add(y), 1).remainder; + return divMod32(x.add(y), 64).remainder; } -function divMod64(n: Field, quotientBits = 64) { +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 << 128n, + `n needs to fit into 128 bit, but got ${n.toBigInt()}` + ); let nBigInt = n.toBigInt(); let q = nBigInt >> 64n; let r = nBigInt - (q << 64n); @@ -90,5 +104,5 @@ function divMod64(n: Field, quotientBits = 64) { } function addMod64(x: Field, y: Field) { - return divMod64(x.add(y), 1).remainder; + return divMod64(x.add(y), 128).remainder; } \ No newline at end of file diff --git a/src/lib/provable/gadgets/sha256.ts b/src/lib/provable/gadgets/sha256.ts index ee7529805..cd8e3c5b2 100644 --- a/src/lib/provable/gadgets/sha256.ts +++ b/src/lib/provable/gadgets/sha256.ts @@ -267,13 +267,13 @@ function sha256Compression(H: UInt32[], W: UInt32[]) { g = f; f = e; e = UInt32.Unsafe.fromField( - divMod32(d.value.add(unreducedT1), 16).remainder + divMod32(d.value.add(unreducedT1), 48).remainder ); // mod 32bit the unreduced field element d = c; c = b; b = a; a = UInt32.Unsafe.fromField( - divMod32(unreducedT2.add(unreducedT1), 16).remainder + divMod32(unreducedT2.add(unreducedT1), 48).remainder ); // mod 32bit } @@ -309,7 +309,7 @@ function createMessageSchedule(M: UInt32[]) { .add(DeltaZero(W[t - 15]).value.add(W[t - 16].value)); // mod 32bit the unreduced field element - W[t] = UInt32.Unsafe.fromField(divMod32(unreduced, 16).remainder); + W[t] = UInt32.Unsafe.fromField(divMod32(unreduced, 48).remainder); } return W; From 10836c95a11edbf12449f1080743c412caee990d Mon Sep 17 00:00:00 2001 From: boray Date: Sat, 27 Jul 2024 01:58:06 +0300 Subject: [PATCH 09/38] add UInt32.or() --- src/lib/provable/int.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/lib/provable/int.ts b/src/lib/provable/int.ts index 7cbf3d381..41b4f76de 100644 --- a/src/lib/provable/int.ts +++ b/src/lib/provable/int.ts @@ -410,7 +410,7 @@ 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. * @@ -887,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. */ From 00cdb753d6c731fc6d6be7a2dc79294b402d42b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boray=20Sayg=C4=B1l=C4=B1er?= Date: Sun, 28 Jul 2024 15:33:14 +0300 Subject: [PATCH 10/38] Update src/lib/provable/gadgets/bitwise.ts Co-authored-by: Martin Minkov --- src/lib/provable/gadgets/bitwise.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/provable/gadgets/bitwise.ts b/src/lib/provable/gadgets/bitwise.ts index 180e8abbd..0ee9e73ad 100644 --- a/src/lib/provable/gadgets/bitwise.ts +++ b/src/lib/provable/gadgets/bitwise.ts @@ -177,7 +177,7 @@ function and(a: Field, b: Field, length: number) { function or(a: Field, b: Field, length: number) { // Validate at 240 bits to ensure padLength (next multiple of 16) doesn't exceed 254 bits, // preventing potential underconstraint issues in the circuit - validateBitLength(length, 240, 'not'); + validateBitLength(length, 240, 'or'); // obtain pad length until the length is a multiple of 16 for n-bit length lookup table let padLength = Math.ceil(length / 16) * 16; From 5db6974c20a3079983df945bee4c05f7e2080e97 Mon Sep 17 00:00:00 2001 From: boray Date: Sun, 28 Jul 2024 16:30:46 +0300 Subject: [PATCH 11/38] update vk-regression.json --- tests/vk-regression/vk-regression.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index fd090840d..e51841019 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -210,8 +210,8 @@ "digest": "afa1f9920f1f657ab015c02f9b2f6c52" }, "BLAKE2B": { - "rows": 5072, - "digest": "368d3b3888b60bb3676b855e8b328dc9" + "rows": 7036, + "digest": "80a6b114f54cb1532a73f5b64bcf7924" } }, "verificationKey": { @@ -296,29 +296,29 @@ } }, "sha256": { - "digest": "38eaa6264b1e4c1c0b5787c06efd92aa7855890aeeafed91818d466f1b109ca5", + "digest": "2502d4e6456fcb0b405f2bf995b34bd1d703951bcd0d461247327d3c86335691", "methods": { "sha256": { - "rows": 5036, - "digest": "c3952f2bd0dbb6b16ed43add7c57c9c5" + "rows": 5048, + "digest": "acab77602b547b806352f2909edde0c7" } }, "verificationKey": { - "data": "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", - "hash": "14970780915659850828924502346680433225700971894695273700497112528587195815959" + "data": "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", + "hash": "23374401081994212237571755249263860858603135551053905255406247748958362756983" } }, "blake2b": { - "digest": "1203b1ff47e8fee2703a9857ef079b9264fe4bd7c263c1db5079e722203d3c4", + "digest": "4f86f666080a04b30e90a5b8fb9cd4f16b0e0474c5aea64a85cb7d95f049440", "methods": { "blake2b": { - "rows": 4479, - "digest": "cb05769ba27e28cfa8ef3c04c043cf81" + "rows": 6415, + "digest": "a25f079dbde80a18c6ed9fa381a6e6c2" } }, "verificationKey": { - "data": "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", - "hash": "23122100856240843309657022506980859401916791014549689915478926277345430629799" + "data": "AABGuUK/LF9vl6gvnxbhLIfHR/lGxdSBIrr2H1Pos4RwNXJhmWu8TE6yUSkOWCpwlIFHmdsm6o9/HroYuXJej9kQXsTQ5gG8UUkS+mGS+K4ksZEBRC/1mavPExktP62UgDorcitvVNv7X7RWyxFw+zyLWEUF1KDw0DRt/7jH3MB6HhdQW899wDPnioPvzD8KhsqBc7/pW3rgMrq0Tr2l+/cawpzvy8ZotXkd2R2DzvEpLlfhIfoaRbUtyr6QrZEfzz+4AD9TVudXsjh2/r92g8vqQ4lQDxyYcWP5Ubv2mtUJHXrde6XaMre4RU/nzfDzWeBMkjACjwtJD9JXq3R7eskMsdOofEqe7HbfaAy8na2pEiEHLwFzvEHORKMvBOITEAEmSOdrEHbZgpQKnIXLxmEiVwIPe2lfGPJlcpHfSRHlIu0op4Q3EWtDzk3YQ9nF44zha4gKpydG7nMoQZHlJwoDg6KsVKEkqUah6s3buhk4VpDXFT5T6zq/DosJPyETJyUob30JIUYZfXSdbHzayDlB11bXlTK0jWmbz1Ca9Sa8Le8b4b7cMXmARvqB9Hs8UyfBtuZPSub4/MTGpJpeAG0RAI0dxxj2Gs+0PMCV5TpzE+LXzlE5mUOUhwI0eU2t4x8SKTY8+Y0gFGuli2Tlo6sZt8lqAKmneA/2KmwP0g2/NCr8cKeYvxugsRKAeZPPVlniwNvZzWfh2E1daUQUOezNAAINAgsqYfdEZ+5XVgpMDBRlM90P4CzT3dHIvAgR1A0i4DzgB+lYDoQoKchH8BiN7GrmpR/5+CW4BID40+WAIwFaRkM8dApKTDsYBhmtMV/sgxqrQcDXZNwqBk6mCCk3KpgRkzfg2Tc3gQOI0dPBzNJp4BCq64BkbVMJVv8gLcIAUeBIUvQeAd4e/6c6Hda07eStRkMjd/pjcOKsHDw4qiGWcxIrcVJuH3JaSvGaYBQfgrg3BObrlWqlcMgi614WOqhX+P2EDnJQ2qU049vEcQjQfjj3H9Cd9MkD9cJDpXkVbwjaFHoqUjLF03j77UXcDKxaHEYXIj2l+rMZmORwQxeCGbbNH+5J37UhUERHJ5FxHM1nauRIFK4l7YkNtDvpAoL5ljIcTAY4TqTdIQTxmn23/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": "5689998639571672758597329327777176318472944579868346888919717700241428393588" } }, "diverse": { From 63b939cf190a56a6bb06e94391fb1cfb68c7fa5d Mon Sep 17 00:00:00 2001 From: boray Date: Mon, 29 Jul 2024 13:12:31 +0300 Subject: [PATCH 12/38] fixes and styling --- src/examples/crypto/blake2b/blake2b.ts | 2 +- src/lib/provable/crypto/hash.ts | 3 +-- src/lib/provable/gadgets/blake2b.ts | 4 ++-- src/lib/provable/gadgets/gadgets.ts | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/examples/crypto/blake2b/blake2b.ts b/src/examples/crypto/blake2b/blake2b.ts index 24f4c5816..1a824f5a5 100644 --- a/src/examples/crypto/blake2b/blake2b.ts +++ b/src/examples/crypto/blake2b/blake2b.ts @@ -11,7 +11,7 @@ let BLAKE2BProgram = ZkProgram({ blake2b: { privateInputs: [Bytes12], async method(xs: Bytes12) { - return Gadgets.BLAKE2B.hash(xs,32); + return Gadgets.BLAKE2B.hash(xs, 32); }, }, }, diff --git a/src/lib/provable/crypto/hash.ts b/src/lib/provable/crypto/hash.ts index 1f16e1ed9..ad278deb3 100644 --- a/src/lib/provable/crypto/hash.ts +++ b/src/lib/provable/crypto/hash.ts @@ -2,7 +2,6 @@ import { Gadgets } from '../gadgets/gadgets.js'; import { Poseidon } from './poseidon.js'; import { Keccak } from './keccak.js'; import { Bytes } from '../wrapped-classes.js'; -import { BLAKE2 } from 'node_modules/@noble/hashes/_blake2.js'; export { Hash }; @@ -144,5 +143,5 @@ const Hash = { hash(bytes: Bytes) { return Gadgets.BLAKE2B.hash(bytes); }, - } + }, }; diff --git a/src/lib/provable/gadgets/blake2b.ts b/src/lib/provable/gadgets/blake2b.ts index 77992ca38..2c040d103 100644 --- a/src/lib/provable/gadgets/blake2b.ts +++ b/src/lib/provable/gadgets/blake2b.ts @@ -32,8 +32,8 @@ const BLAKE2BConstants = { }; const BLAKE2B = { - hash(data: FlexibleBytes, digest_length = 64) { - const state = initialize(digest_length); + hash(data: FlexibleBytes, digestLength = 64) { + const state = initialize(digestLength); update(state, Bytes.from(data).bytes); const out = final(state); return Bytes.from(out); diff --git a/src/lib/provable/gadgets/gadgets.ts b/src/lib/provable/gadgets/gadgets.ts index 5eeb2a547..5043d1035 100644 --- a/src/lib/provable/gadgets/gadgets.ts +++ b/src/lib/provable/gadgets/gadgets.ts @@ -466,7 +466,7 @@ const Gadgets = { * let a = Field.from(3); // ... 000011 * let b = Field.from(5); // ... 000101 * - * let c = Gadgets.or(b); // ... 000111 + * let c = Gadgets.or(a, b, 16); // ... 000111 * c.assertEquals(7); * ``` */ From e6317f12f36e5f68574c6248286d2f4bb3a42a2f Mon Sep 17 00:00:00 2001 From: boray Date: Tue, 30 Jul 2024 04:01:14 +0300 Subject: [PATCH 13/38] remove question marks --- src/lib/provable/gadgets/blake2b.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/provable/gadgets/blake2b.ts b/src/lib/provable/gadgets/blake2b.ts index 2c040d103..88d1e98aa 100644 --- a/src/lib/provable/gadgets/blake2b.ts +++ b/src/lib/provable/gadgets/blake2b.ts @@ -149,7 +149,7 @@ function update( ): void { for (let i = 0; i < input.length; i++) { if (state.buflen === 128) { - state.t[0] = state.t[0].add(128); // ? + state.t[0] = state.t[0].add(128); if (state.t[0].equals(UInt64.zero)) { state.t[1] = state.t[1].addMod64(UInt64.one); } @@ -168,7 +168,7 @@ function final(state: { buflen: number; outlen: number; }): UInt8[] { - state.t[0] = state.t[0].add(state.buflen); // ? + state.t[0] = state.t[0].add(state.buflen); if (state.t[0].equals(UInt64.zero)) { state.t[1] = state.t[1].add(UInt64.zero); } From 90daf62c9af6977a7dee90ae8d3c1c0f91ba4381 Mon Sep 17 00:00:00 2001 From: boray Date: Wed, 21 Aug 2024 15:05:37 +0300 Subject: [PATCH 14/38] optimize g function additions --- src/lib/provable/gadgets/blake2b.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/lib/provable/gadgets/blake2b.ts b/src/lib/provable/gadgets/blake2b.ts index 88d1e98aa..27ddaefb8 100644 --- a/src/lib/provable/gadgets/blake2b.ts +++ b/src/lib/provable/gadgets/blake2b.ts @@ -2,6 +2,7 @@ import { UInt64, UInt8 } from '../int.js'; import { FlexibleBytes } from '../bytes.js'; import { Bytes } from '../wrapped-classes.js'; +import { Gadgets } from './gadgets.js'; export { BLAKE2B }; @@ -72,16 +73,16 @@ function G( x: UInt64, y: UInt64 ) { - v[a] = v[a].addMod64(v[b].addMod64(x)); + 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] = v[c].addMod64(v[d]); + + 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] = v[a].addMod64(v[b].addMod64(y)); + 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] = v[c].addMod64(v[d]); + 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'); } From 1938405ae3820d45695be52fdaede122bdf59b3f Mon Sep 17 00:00:00 2001 From: boray Date: Wed, 21 Aug 2024 15:30:04 +0300 Subject: [PATCH 15/38] hardcode IV as UInt64 --- src/lib/provable/gadgets/blake2b.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/lib/provable/gadgets/blake2b.ts b/src/lib/provable/gadgets/blake2b.ts index 27ddaefb8..11d972078 100644 --- a/src/lib/provable/gadgets/blake2b.ts +++ b/src/lib/provable/gadgets/blake2b.ts @@ -8,14 +8,14 @@ export { BLAKE2B }; const BLAKE2BConstants = { IV: [ - 0x6a09e667f3bcc908n, - 0xbb67ae8584caa73bn, - 0x3c6ef372fe94f82bn, - 0xa54ff53a5f1d36f1n, - 0x510e527fade682d1n, - 0x9b05688c2b3e6c1fn, - 0x1f83d9abfb41bd6bn, - 0x5be0cd19137e2179n, + 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: [ @@ -40,7 +40,7 @@ const BLAKE2B = { return Bytes.from(out); }, get IV() { - return BLAKE2BConstants.IV.map((x) => UInt64.from(x)); + return BLAKE2BConstants.IV; }, }; From 5e2b28a2527cd858c20b90736f8bae89b79a728f Mon Sep 17 00:00:00 2001 From: boray Date: Wed, 21 Aug 2024 16:41:20 +0300 Subject: [PATCH 16/38] dump vks --- tests/vk-regression/vk-regression.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/vk-regression/vk-regression.json b/tests/vk-regression/vk-regression.json index e51841019..963491c33 100644 --- a/tests/vk-regression/vk-regression.json +++ b/tests/vk-regression/vk-regression.json @@ -210,8 +210,8 @@ "digest": "afa1f9920f1f657ab015c02f9b2f6c52" }, "BLAKE2B": { - "rows": 7036, - "digest": "80a6b114f54cb1532a73f5b64bcf7924" + "rows": 6012, + "digest": "85a6d64774a513dbff3dd6a8fc3db76c" } }, "verificationKey": { @@ -296,29 +296,29 @@ } }, "sha256": { - "digest": "2502d4e6456fcb0b405f2bf995b34bd1d703951bcd0d461247327d3c86335691", + "digest": "38eaa6264b1e4c1c0b5787c06efd92aa7855890aeeafed91818d466f1b109ca5", "methods": { "sha256": { - "rows": 5048, - "digest": "acab77602b547b806352f2909edde0c7" + "rows": 5036, + "digest": "c3952f2bd0dbb6b16ed43add7c57c9c5" } }, "verificationKey": { - "data": "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", - "hash": "23374401081994212237571755249263860858603135551053905255406247748958362756983" + "data": "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", + "hash": "14970780915659850828924502346680433225700971894695273700497112528587195815959" } }, "blake2b": { - "digest": "4f86f666080a04b30e90a5b8fb9cd4f16b0e0474c5aea64a85cb7d95f049440", + "digest": "3d1be0b145513226d94cf098242446abf4ce399c0b75556e3f638fe5fcedd59", "methods": { "blake2b": { - "rows": 6415, - "digest": "a25f079dbde80a18c6ed9fa381a6e6c2" + "rows": 5408, + "digest": "ff73fb592262ef3a6b665a803052348f" } }, "verificationKey": { - "data": "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", - "hash": "5689998639571672758597329327777176318472944579868346888919717700241428393588" + "data": "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", + "hash": "16086804604944857114083318777569758960347127563430581910767940324520536993210" } }, "diverse": { From a86a106531c05bd6f4e2b4a4b40ba7be60fde0cf Mon Sep 17 00:00:00 2001 From: boray Date: Fri, 23 Aug 2024 02:29:10 +0300 Subject: [PATCH 17/38] simplify or --- src/lib/provable/gadgets/bitwise.ts | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/src/lib/provable/gadgets/bitwise.ts b/src/lib/provable/gadgets/bitwise.ts index 0ee9e73ad..425cf54f2 100644 --- a/src/lib/provable/gadgets/bitwise.ts +++ b/src/lib/provable/gadgets/bitwise.ts @@ -175,32 +175,7 @@ function and(a: Field, b: Field, length: number) { } function or(a: Field, b: Field, length: number) { - // Validate at 240 bits to ensure padLength (next multiple of 16) doesn't exceed 254 bits, - // preventing potential underconstraint issues in the circuit - validateBitLength(length, 240, 'or'); - - // obtain pad length until the length is a multiple of 16 for n-bit length lookup table - let padLength = Math.ceil(length / 16) * 16; - - // handle constant case - if (a.isConstant() && b.isConstant()) { - let max = 1n << BigInt(padLength); - - assert(a.toBigInt() < max, `${a} does not fit into ${padLength} bits`); - assert(b.toBigInt() < max, `${b} does not fit into ${padLength} bits`); - - return new Field(a.toBigInt() | b.toBigInt()); - } - - // calculate expect and output - let outputOr = Provable.witness(Field, () => a.toBigInt() | b.toBigInt()); - - outputOr.assertEquals( - not(and(not(a, length), not(b, length), length), length) - ); - - // return the result of the or operation - return outputOr; + return not(and(not(a, length), not(b, length), length), length); } function rotate64( From c38118e7917087cecae4f8e78226c79563968ab5 Mon Sep 17 00:00:00 2001 From: boray Date: Fri, 23 Aug 2024 02:36:22 +0300 Subject: [PATCH 18/38] fix formatting --- src/lib/provable/gadgets/arithmetic.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lib/provable/gadgets/arithmetic.ts b/src/lib/provable/gadgets/arithmetic.ts index d8f228fdf..ceec7281b 100644 --- a/src/lib/provable/gadgets/arithmetic.ts +++ b/src/lib/provable/gadgets/arithmetic.ts @@ -57,9 +57,11 @@ 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}`) + 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); @@ -105,4 +107,4 @@ function divMod64(n: Field, nBits = 128) { function addMod64(x: Field, y: Field) { return divMod64(x.add(y), 128).remainder; -} \ No newline at end of file +} From 5bba7390617d36256f9fb1048667a57b11c74750 Mon Sep 17 00:00:00 2001 From: boray Date: Fri, 23 Aug 2024 02:40:01 +0300 Subject: [PATCH 19/38] fix divMod64 range check --- src/lib/provable/gadgets/arithmetic.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/provable/gadgets/arithmetic.ts b/src/lib/provable/gadgets/arithmetic.ts index ceec7281b..2f49032d4 100644 --- a/src/lib/provable/gadgets/arithmetic.ts +++ b/src/lib/provable/gadgets/arithmetic.ts @@ -59,8 +59,8 @@ function addMod32(x: Field, y: Field) { function divMod64(n: Field, nBits = 128) { assert( - nBits >= 0 && nBits <= 255, - `nBits must be in the range [0, 255], got ${nBits}` + 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 From 555070b88b1013aba3019725be5b556b0d0fbce0 Mon Sep 17 00:00:00 2001 From: boray Date: Fri, 23 Aug 2024 03:06:53 +0300 Subject: [PATCH 20/38] fix elliptic curve to match v2 --- src/lib/provable/gadgets/elliptic-curve.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/provable/gadgets/elliptic-curve.ts b/src/lib/provable/gadgets/elliptic-curve.ts index 574bd9e13..7af03fa9f 100644 --- a/src/lib/provable/gadgets/elliptic-curve.ts +++ b/src/lib/provable/gadgets/elliptic-curve.ts @@ -11,6 +11,7 @@ import { } from '../../../bindings/crypto/bigint-helpers.js'; import { CurveAffine, + GroupAffine, affineAdd, affineDouble, } from '../../../bindings/crypto/elliptic-curve.js'; @@ -381,7 +382,7 @@ function multiScalarMulConstant( // TODO dedicated MSM let s = scalars.map(Field3.toBigint); let P = points.map(Point.toBigint); - let sum = Curve.zero; + let sum: GroupAffine = Curve.zero; for (let i = 0; i < n; i++) { if (useGlv) { sum = Curve.add(sum, Curve.Endo.scale(P[i], s[i])); @@ -800,4 +801,4 @@ function reduceMrcStack(xs: Field[]) { remaining[i] = xs[3 * nFull + i]; } multiRangeCheck(remaining); -} +} \ No newline at end of file From d071e7b6fca3c00966641c4a38f0eb6f0e1b56d8 Mon Sep 17 00:00:00 2001 From: boray Date: Fri, 23 Aug 2024 03:22:56 +0300 Subject: [PATCH 21/38] fix formatting --- src/lib/provable/gadgets/elliptic-curve.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/provable/gadgets/elliptic-curve.ts b/src/lib/provable/gadgets/elliptic-curve.ts index 7af03fa9f..68b01415a 100644 --- a/src/lib/provable/gadgets/elliptic-curve.ts +++ b/src/lib/provable/gadgets/elliptic-curve.ts @@ -801,4 +801,4 @@ function reduceMrcStack(xs: Field[]) { remaining[i] = xs[3 * nFull + i]; } multiRangeCheck(remaining); -} \ No newline at end of file +} From 9b32c4daaf2db4f0c3f8a06e35d7e7037b7b9061 Mon Sep 17 00:00:00 2001 From: boray Date: Thu, 29 Aug 2024 04:38:10 +0300 Subject: [PATCH 22/38] style(gadgets.ts): wrap comments for readability --- src/lib/provable/gadgets/gadgets.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/lib/provable/gadgets/gadgets.ts b/src/lib/provable/gadgets/gadgets.ts index e2e9c5102..3a1b86f4a 100644 --- a/src/lib/provable/gadgets/gadgets.ts +++ b/src/lib/provable/gadgets/gadgets.ts @@ -420,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`\ @@ -431,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. * @@ -454,7 +457,9 @@ const Gadgets = { * 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. + * 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. * From 70169e37919bbb9f73e4753c93c7640fc2381f88 Mon Sep 17 00:00:00 2001 From: boray Date: Thu, 5 Sep 2024 16:19:08 +0300 Subject: [PATCH 23/38] fix(bitwise.unit-test.ts): correct typo --- src/lib/provable/test/bitwise.unit-test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/provable/test/bitwise.unit-test.ts b/src/lib/provable/test/bitwise.unit-test.ts index 74e504af1..c2498c6b3 100644 --- a/src/lib/provable/test/bitwise.unit-test.ts +++ b/src/lib/provable/test/bitwise.unit-test.ts @@ -197,7 +197,7 @@ 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; + return x | y; }, async (x, y) => { let proof = await Bitwise.or(x, y); From acd32bb849a89a4be600eed881647b8c18734657 Mon Sep 17 00:00:00 2001 From: boray Date: Thu, 5 Sep 2024 16:35:04 +0300 Subject: [PATCH 24/38] perf: optimize divmod rangechecks --- src/lib/provable/gadgets/arithmetic.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/provable/gadgets/arithmetic.ts b/src/lib/provable/gadgets/arithmetic.ts index 29396903e..72946cb78 100644 --- a/src/lib/provable/gadgets/arithmetic.ts +++ b/src/lib/provable/gadgets/arithmetic.ts @@ -40,6 +40,8 @@ function divMod32(n: Field, nBits = 64) { if (quotientBits === 1) { quotient.assertBool(); + } else if (quotientBits === 32) { + rangeCheck32(quotient); } else { rangeCheckN(quotientBits, quotient); } @@ -92,6 +94,8 @@ function divMod64(n: Field, nBits = 128) { if (quotientBits === 1) { quotient.assertBool(); + } else if (quotientBits === 64) { + rangeCheck64(quotient); } else { rangeCheckN(quotientBits, quotient); } From ec5b6a79069195ed526afb917173289c35d343fd Mon Sep 17 00:00:00 2001 From: boray Date: Thu, 5 Sep 2024 18:22:14 +0300 Subject: [PATCH 25/38] fix: remove incorrect optimization --- src/lib/provable/gadgets/arithmetic.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib/provable/gadgets/arithmetic.ts b/src/lib/provable/gadgets/arithmetic.ts index 72946cb78..17250ccd7 100644 --- a/src/lib/provable/gadgets/arithmetic.ts +++ b/src/lib/provable/gadgets/arithmetic.ts @@ -40,8 +40,6 @@ function divMod32(n: Field, nBits = 64) { if (quotientBits === 1) { quotient.assertBool(); - } else if (quotientBits === 32) { - rangeCheck32(quotient); } else { rangeCheckN(quotientBits, quotient); } From b7410274c5c872e2cab89889d60701bd23f30c68 Mon Sep 17 00:00:00 2001 From: boray Date: Fri, 6 Sep 2024 19:47:24 +0300 Subject: [PATCH 26/38] fix(arithmetic.ts): fix range assertion --- src/lib/provable/gadgets/arithmetic.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/provable/gadgets/arithmetic.ts b/src/lib/provable/gadgets/arithmetic.ts index 17250ccd7..99309bc5f 100644 --- a/src/lib/provable/gadgets/arithmetic.ts +++ b/src/lib/provable/gadgets/arithmetic.ts @@ -68,8 +68,8 @@ function divMod64(n: Field, nBits = 128) { if (n.isConstant()) { assert( - n.toBigInt() < 1n << 128n, - `n needs to fit into 128 bit, but got ${n.toBigInt()}` + n.toBigInt() < 1n << BigInt(nBits), + `n needs to fit into ${nBits} bits, but got ${n.toBigInt()}` ); let nBigInt = n.toBigInt(); let q = nBigInt >> 64n; @@ -108,5 +108,5 @@ function divMod64(n: Field, nBits = 128) { } function addMod64(x: Field, y: Field) { - return divMod64(x.add(y), 128).remainder; + return divMod64(x.add(y), 65).remainder; } From a34d8c2fcd97d94085578a9a753589385cbf5b97 Mon Sep 17 00:00:00 2001 From: boray Date: Fri, 6 Sep 2024 22:02:56 +0300 Subject: [PATCH 27/38] docs: clarify output description --- src/lib/provable/gadgets/gadgets.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/provable/gadgets/gadgets.ts b/src/lib/provable/gadgets/gadgets.ts index 3a1b86f4a..dd775ebe1 100644 --- a/src/lib/provable/gadgets/gadgets.ts +++ b/src/lib/provable/gadgets/gadgets.ts @@ -962,7 +962,7 @@ const Gadgets = { * 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 bit length. + * Produces an output of {@link Bytes} that conforms to the chosen digest length. * * @param data - {@link Bytes} representing the message to hash. * From 6ba2968ba61b0c2ecd9599aba3cd31fcb7a3426a Mon Sep 17 00:00:00 2001 From: boray Date: Sat, 7 Sep 2024 19:23:12 +0300 Subject: [PATCH 28/38] fix(blake2b.ts): add range check for digestLength --- src/lib/provable/gadgets/blake2b.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/provable/gadgets/blake2b.ts b/src/lib/provable/gadgets/blake2b.ts index 11d972078..c9b717823 100644 --- a/src/lib/provable/gadgets/blake2b.ts +++ b/src/lib/provable/gadgets/blake2b.ts @@ -3,6 +3,7 @@ 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'; export { BLAKE2B }; @@ -34,6 +35,10 @@ const BLAKE2BConstants = { const BLAKE2B = { hash(data: FlexibleBytes, digestLength = 64) { + assert( + digestLength >= 1 && digestLength <= 64, + `digestLength must be in the range [1, 64], got ${digestLength}` + ); const state = initialize(digestLength); update(state, Bytes.from(data).bytes); const out = final(state); From d841a51ff15c44cb3fe4fbb2a8f312095c572fdc Mon Sep 17 00:00:00 2001 From: boray Date: Sat, 7 Sep 2024 19:33:44 +0300 Subject: [PATCH 29/38] fix(blake2b.ts): range check input byte length --- src/lib/provable/gadgets/blake2b.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/provable/gadgets/blake2b.ts b/src/lib/provable/gadgets/blake2b.ts index c9b717823..d635535e0 100644 --- a/src/lib/provable/gadgets/blake2b.ts +++ b/src/lib/provable/gadgets/blake2b.ts @@ -39,6 +39,10 @@ const BLAKE2B = { 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); update(state, Bytes.from(data).bytes); const out = final(state); From d999c09f0d052c75541455f718a8dc9a75cfb86f Mon Sep 17 00:00:00 2001 From: boray Date: Mon, 9 Sep 2024 04:33:10 +0300 Subject: [PATCH 30/38] test: add test vector --- src/lib/provable/test/blake2b.unit-test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lib/provable/test/blake2b.unit-test.ts b/src/lib/provable/test/blake2b.unit-test.ts index d94b8bf96..d45a6341e 100644 --- a/src/lib/provable/test/blake2b.unit-test.ts +++ b/src/lib/provable/test/blake2b.unit-test.ts @@ -50,6 +50,11 @@ function testVectors() { 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', From e5bd5746187ace3d7f88b3fcd494eaa9d3c2930c Mon Sep 17 00:00:00 2001 From: boray Date: Mon, 9 Sep 2024 04:34:41 +0300 Subject: [PATCH 31/38] feat: add last block flag and state type --- src/lib/provable/gadgets/blake2b.ts | 42 ++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/lib/provable/gadgets/blake2b.ts b/src/lib/provable/gadgets/blake2b.ts index d635535e0..c37fde6b7 100644 --- a/src/lib/provable/gadgets/blake2b.ts +++ b/src/lib/provable/gadgets/blake2b.ts @@ -7,6 +7,14 @@ import { assert } from '../../util/errors.js'; export { BLAKE2B }; +type State = { + h: UInt64[]; + t: UInt64[]; + buf: UInt8[]; + buflen: number; + outlen: number; +}; + const BLAKE2BConstants = { IV: [ UInt64.from(0x6a09e667f3bcc908n), @@ -44,8 +52,8 @@ const BLAKE2B = { `data byte length must be in the range [0, 2**128), got ${data.length}` ); const state = initialize(digestLength); - update(state, Bytes.from(data).bytes); - const out = final(state); + const updated_state = update(state, Bytes.from(data).bytes); + const out = final(updated_state); return Bytes.from(out); }, get IV() { @@ -99,17 +107,24 @@ function compress(state: { h: UInt64[]; t: UInt64[]; f: UInt64[]; - buf: UInt8[]; - buflen: number; - outlen: number; -}): void { - const { h, t, f, buf } = state; +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 - v[14] = v[14].xor(f[0]); - v[15] = v[15].xor(f[1]); + if (last) { + v[14] = v[14].not(); + } const m: UInt64[] = []; for (let i = 0; i < 16; i++) { @@ -142,8 +157,10 @@ function compress(state: { } for (let i = 0; i < 8; i++) { + // XOR the two halves h[i] = v[i].xor(v[i + 8]).xor(h[i]); } + return state; } function update( @@ -156,18 +173,19 @@ function update( outlen: number; }, input: UInt8[] -): void { +): State { for (let i = 0; i < input.length; i++) { if (state.buflen === 128) { state.t[0] = state.t[0].add(128); if (state.t[0].equals(UInt64.zero)) { state.t[1] = state.t[1].addMod64(UInt64.one); } - compress(state); + state = compress(state, false); state.buflen = 0; } state.buf[state.buflen++] = input[i]; } + return state; } function final(state: { @@ -187,7 +205,7 @@ function final(state: { while (state.buflen < 128) { state.buf[state.buflen++] = UInt8.from(0); } - compress(state); + compress(state, true); const out: UInt8[] = []; for (let i = 0; i < state.outlen; i++) { From 1c5f3bd5a498c77d8cf225e39067ec2cee478aea Mon Sep 17 00:00:00 2001 From: boray Date: Mon, 9 Sep 2024 04:35:33 +0300 Subject: [PATCH 32/38] fix: counter logic --- src/lib/provable/gadgets/blake2b.ts | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/lib/provable/gadgets/blake2b.ts b/src/lib/provable/gadgets/blake2b.ts index c37fde6b7..69341b5b3 100644 --- a/src/lib/provable/gadgets/blake2b.ts +++ b/src/lib/provable/gadgets/blake2b.ts @@ -4,6 +4,7 @@ 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 }; @@ -176,8 +177,8 @@ function update( ): State { for (let i = 0; i < input.length; i++) { if (state.buflen === 128) { - state.t[0] = state.t[0].add(128); - if (state.t[0].equals(UInt64.zero)) { + state.t[0] = state.t[0].addMod64(UInt64.from(state.buflen)); + if (state.t[0].toBigInt() < state.buflen) { state.t[1] = state.t[1].addMod64(UInt64.one); } state = compress(state, false); @@ -196,12 +197,19 @@ function final(state: { buflen: number; outlen: number; }): UInt8[] { - state.t[0] = state.t[0].add(state.buflen); - if (state.t[0].equals(UInt64.zero)) { - state.t[1] = state.t[1].add(UInt64.zero); + state.t[0] = state.t[0].addMod64(UInt64.from(state.buflen)); + if (state.t[0].toBigInt() < state.buflen) { + state.t[1] = state.t[1].addMod64(UInt64.one); } - state.f[0] = UInt64.from('0xFFFFFFFFFFFFFFFF'); - + /* + 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); } From 7f650b147f1857dceb4f3c9ea0ed40824cd2c999 Mon Sep 17 00:00:00 2001 From: boray Date: Mon, 9 Sep 2024 04:38:29 +0300 Subject: [PATCH 33/38] chore: update reference --- src/lib/provable/gadgets/blake2b.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/provable/gadgets/blake2b.ts b/src/lib/provable/gadgets/blake2b.ts index 69341b5b3..0b4e8dad8 100644 --- a/src/lib/provable/gadgets/blake2b.ts +++ b/src/lib/provable/gadgets/blake2b.ts @@ -1,4 +1,4 @@ -// https://www.blake2.net/blake2.pdf +// 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'; From e545799e12ae499c53e579de06c529951643f841 Mon Sep 17 00:00:00 2001 From: boray Date: Mon, 9 Sep 2024 05:21:12 +0300 Subject: [PATCH 34/38] docs: add comments --- src/lib/provable/gadgets/blake2b.ts | 99 +++++++++++++++++++---------- 1 file changed, 66 insertions(+), 33 deletions(-) diff --git a/src/lib/provable/gadgets/blake2b.ts b/src/lib/provable/gadgets/blake2b.ts index 0b4e8dad8..be163e6f3 100644 --- a/src/lib/provable/gadgets/blake2b.ts +++ b/src/lib/provable/gadgets/blake2b.ts @@ -62,26 +62,17 @@ const BLAKE2B = { }, }; -function initialize(outlen: number): { - h: UInt64[]; - t: UInt64[]; - f: 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]); - return { - h, - t: [UInt64.zero, UInt64.zero], - f: [UInt64.zero, UInt64.zero], - buf: [], - buflen: 0, - outlen, - }; -} - +/** + * 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, @@ -104,10 +95,11 @@ function G( v[b] = v[b].xor(v[c]).rotate(63, 'right'); } -function compress(state: { - h: UInt64[]; - t: UInt64[]; - f: UInt64[]; +/** + * Compression function. "last" flag indicates last block. + * @param {State} state + * @param {boolean} last + */ function compress( state: { h: UInt64[]; @@ -123,12 +115,15 @@ function compress( 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.from( buf[i * 8] @@ -145,6 +140,7 @@ function compress( } 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]]); @@ -164,11 +160,40 @@ function compress( 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[]; - f: UInt64[]; buf: UInt8[]; buflen: number; outlen: number; @@ -177,29 +202,36 @@ function update( ): State { for (let i = 0; i < input.length; i++) { if (state.buflen === 128) { - state.t[0] = state.t[0].addMod64(UInt64.from(state.buflen)); + // buffer full ? + state.t[0] = state.t[0].addMod64(UInt64.from(state.buflen)); // add counters if (state.t[0].toBigInt() < state.buflen) { - state.t[1] = state.t[1].addMod64(UInt64.one); + // carry overflow ? + state.t[1] = state.t[1].addMod64(UInt64.one); // high word } - state = compress(state, false); - state.buflen = 0; + 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[]; - f: UInt64[]; buf: UInt8[]; buflen: number; outlen: number; }): UInt8[] { - state.t[0] = state.t[0].addMod64(UInt64.from(state.buflen)); + state.t[0] = state.t[0].addMod64(UInt64.from(state.buflen)); // mark last block offset if (state.t[0].toBigInt() < state.buflen) { - state.t[1] = state.t[1].addMod64(UInt64.one); + // carry overflow ? + state.t[1] = state.t[1].addMod64(UInt64.one); // high word } /* state.t[1] = state.t[1].add( @@ -211,10 +243,11 @@ function final(state: { ); */ while (state.buflen < 128) { - state.buf[state.buflen++] = UInt8.from(0); + 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( From 368ae88799bfe6f30defd83da4fd574d0ccdf2e8 Mon Sep 17 00:00:00 2001 From: boray Date: Wed, 11 Sep 2024 01:56:20 +0300 Subject: [PATCH 35/38] docs: add more comments --- src/lib/provable/gadgets/blake2b.ts | 45 ++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/src/lib/provable/gadgets/blake2b.ts b/src/lib/provable/gadgets/blake2b.ts index be163e6f3..cb6717094 100644 --- a/src/lib/provable/gadgets/blake2b.ts +++ b/src/lib/provable/gadgets/blake2b.ts @@ -8,6 +8,22 @@ 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[]; @@ -82,16 +98,24 @@ function G( x: UInt64, y: UInt64 ) { - v[a] = UInt64.Unsafe.fromField(Gadgets.divMod64(v[a].value.add(v[b].value.add(x.value)), 128).remainder); + 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[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[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[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'); } @@ -161,11 +185,11 @@ function compress( } /** -* Initializes the state with the given digest length. -* -* @param {number} outlen - Digest length in bits -* @returns {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[]; @@ -184,6 +208,7 @@ function initialize(outlen: number): { outlen, }; } + /** * Updates hash state * @param {State} state From cb80ccfa9c1459678723cd4ea2297c43c64288da Mon Sep 17 00:00:00 2001 From: boray Date: Fri, 20 Sep 2024 17:00:29 +0300 Subject: [PATCH 36/38] perf: reduce row number --- src/lib/provable/gadgets/blake2b.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/lib/provable/gadgets/blake2b.ts b/src/lib/provable/gadgets/blake2b.ts index cb6717094..a333fa78d 100644 --- a/src/lib/provable/gadgets/blake2b.ts +++ b/src/lib/provable/gadgets/blake2b.ts @@ -149,16 +149,15 @@ function compress( for (let i = 0; i < 16; i++) { // get little-endian words m.push( - UInt64.from( - buf[i * 8] - .toUInt64() - .or(buf[i * 8 + 1].toUInt64().leftShift(8)) - .or(buf[i * 8 + 2].toUInt64().leftShift(16)) - .or(buf[i * 8 + 3].toUInt64().leftShift(24)) - .or(buf[i * 8 + 4].toUInt64().leftShift(32)) - .or(buf[i * 8 + 5].toUInt64().leftShift(40)) - .or(buf[i * 8 + 6].toUInt64().leftShift(48)) - .or(buf[i * 8 + 7].toUInt64().leftShift(56)) + UInt64.fromFields( + 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)).toFields() ) ); } From 20a0eac16358286fdc3720fcaf295fc0012dab44 Mon Sep 17 00:00:00 2001 From: boray Date: Fri, 20 Sep 2024 22:10:48 +0300 Subject: [PATCH 37/38] fix: uint64 construction --- src/lib/provable/gadgets/blake2b.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/provable/gadgets/blake2b.ts b/src/lib/provable/gadgets/blake2b.ts index a333fa78d..51fb1a3e8 100644 --- a/src/lib/provable/gadgets/blake2b.ts +++ b/src/lib/provable/gadgets/blake2b.ts @@ -149,7 +149,7 @@ function compress( for (let i = 0; i < 16; i++) { // get little-endian words m.push( - UInt64.fromFields( + 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)) @@ -157,7 +157,7 @@ function compress( .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)).toFields() + .add(buf[i * 8 + 7].value.mul(1n << 56n)).seal() ) ); } From cceeef5a4d8f209663d9703b90fdeda54e89f9bd Mon Sep 17 00:00:00 2001 From: boray Date: Fri, 20 Sep 2024 22:14:12 +0300 Subject: [PATCH 38/38] test: remove in-circuit equivalence tests --- src/lib/provable/test/bitwise.unit-test.ts | 118 --------------------- 1 file changed, 118 deletions(-) diff --git a/src/lib/provable/test/bitwise.unit-test.ts b/src/lib/provable/test/bitwise.unit-test.ts index c2498c6b3..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({ @@ -149,116 +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: [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.or(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) {