From 3fdfd5cb4c372bd2b274780db15496e8c245cc34 Mon Sep 17 00:00:00 2001 From: Colin Roberts Date: Thu, 14 Nov 2024 10:18:54 -0700 Subject: [PATCH] feat: nonzero `DataHasher` (#45) * Delete notes.md * feat: updated `DataHasher` * feat: tested `DataHasher` * test: one more example * Update package.json --------- Co-authored-by: Sambhav Dusad --- circuits/http/nivc/notes.md | 5 ---- circuits/test/common/poseidon.ts | 8 +++++-- circuits/test/full/full.test.ts | 2 -- circuits/test/utils/hash.test.ts | 40 ++++++++++++++++++++++++++++---- circuits/utils/hash.circom | 8 ++++--- package.json | 2 +- 6 files changed, 47 insertions(+), 18 deletions(-) delete mode 100644 circuits/http/nivc/notes.md diff --git a/circuits/http/nivc/notes.md b/circuits/http/nivc/notes.md deleted file mode 100644 index a0bd064..0000000 --- a/circuits/http/nivc/notes.md +++ /dev/null @@ -1,5 +0,0 @@ -# JSON Notes - -Okay what we can do is have a hash chain up the stack and store at most MAX_STACK_HEIGHT hash values, then write to this array at the current depth for each new value we get. We also should hash the stack indicator (array vs. object). - -We then just have to assert that we get an `ArrayEqual` with our given input at some point. We also assert the hash of the value itself is correct (possibly this just happens in a uniform way thinking of it as an object itself? Some details remain.) \ No newline at end of file diff --git a/circuits/test/common/poseidon.ts b/circuits/test/common/poseidon.ts index 62509cc..0c2a12d 100644 --- a/circuits/test/common/poseidon.ts +++ b/circuits/test/common/poseidon.ts @@ -88,8 +88,12 @@ export function DataHasher(input: number[]): bigint { packedInput += BigInt(input[16 * i + j]) * BigInt(2 ** (8 * j)); } - // Compute next hash using previous hash and packed input - hashes.push(PoseidonModular([hashes[i], packedInput])); + // Compute next hash using previous hash and packed input, but if packed input is zero, don't hash it. + if (packedInput == BigInt(0)) { + hashes.push(hashes[i]); + } else { + hashes.push(PoseidonModular([hashes[i], packedInput])); + } } // Return the last hash diff --git a/circuits/test/full/full.test.ts b/circuits/test/full/full.test.ts index d87437c..af4f8ea 100644 --- a/circuits/test/full/full.test.ts +++ b/circuits/test/full/full.test.ts @@ -2,8 +2,6 @@ import { assert } from "chai"; import { circomkit, WitnessTester, toByte } from "../common"; import { DataHasher } from "../common/poseidon"; - - // HTTP/1.1 200 OK // content-type: application/json; charset=utf-8 // content-encoding: gzip diff --git a/circuits/test/utils/hash.test.ts b/circuits/test/utils/hash.test.ts index 28c7bde..9d7ac37 100644 --- a/circuits/test/utils/hash.test.ts +++ b/circuits/test/utils/hash.test.ts @@ -1,5 +1,6 @@ +import assert from "assert"; import { circomkit, WitnessTester } from "../common"; -import { PoseidonModular } from "../common/poseidon"; +import { DataHasher, PoseidonModular } from "../common/poseidon"; describe("hash", () => { describe("PoseidonModular_16", () => { @@ -83,10 +84,9 @@ describe("hash", () => { it("witness: in = [0,...x16]", async () => { const input = Array(16).fill(0); - const hash = PoseidonModular([0, 0]); await circuit.expectPass( { in: input }, - { out: hash } + { out: 0 } ); }); @@ -128,8 +128,23 @@ describe("hash", () => { 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 125, 13, 10, 32, 32, 32, 32, 32, 32, 32, 93, 13, 10, 32, 32, 32, 125, 13, 10, 125] + const http_start_line = [ + 72, 84, 84, 80, 47, 49, 46, 49, 32, 50, 48, 48, 32, 79, 75, 13, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + ]; + describe("DataHasherHTTP", () => { let circuit: WitnessTester<["in"], ["out"]>; + let circuit_small: WitnessTester<["in"], ["out"]>; before(async () => { circuit = await circomkit.WitnessTester(`DataHasher`, { @@ -138,13 +153,28 @@ describe("hash", () => { params: [320], }); console.log("#constraints:", await circuit.getConstraintCount()); + + circuit_small = await circomkit.WitnessTester(`DataHasher`, { + file: "utils/hash", + template: "DataHasher", + params: [32], + }); + console.log("#constraints:", await circuit.getConstraintCount()); }); it("witness: TEST HTTP BYTES", async () => { - + let hash = DataHasher(TEST_HTTP_BYTES); + assert.deepEqual(String(hash), "2195365663909569734943279727560535141179588918483111718403427949138562480675"); await circuit.expectPass({ in: TEST_HTTP_BYTES }, { out: "2195365663909569734943279727560535141179588918483111718403427949138562480675" }); }); + let hash = DataHasher(http_start_line); + it("witness: TEST HTTP START LINE MASK", async () => { + await circuit.expectPass({ in: http_start_line }, { out: hash }); + }); + it("witness: TEST HTTP START LINE MASK TRUNCATED", async () => { + await circuit_small.expectPass({ in: http_start_line.slice(0, 32) }, { out: hash }); + }); }); -}); \ No newline at end of file +}); diff --git a/circuits/utils/hash.circom b/circuits/utils/hash.circom index 2c62830..682244c 100644 --- a/circuits/utils/hash.circom +++ b/circuits/utils/hash.circom @@ -72,16 +72,18 @@ template DataHasher(DATA_BYTES) { signal input in[DATA_BYTES]; signal output out; + signal not_to_hash[DATA_BYTES \ 16]; + signal option_hash[DATA_BYTES \ 16]; signal hashes[DATA_BYTES \ 16 + 1]; hashes[0] <== 0; - for(var i = 0 ; i < DATA_BYTES \ 16 ; i++) { var packedInput = 0; for(var j = 0 ; j < 16 ; j++) { packedInput += in[16 * i + j] * 2**(8*j); } - hashes[i+1] <== PoseidonChainer()([hashes[i],packedInput]); + not_to_hash[i] <== IsZero()(packedInput); + option_hash[i] <== PoseidonChainer()([hashes[i],packedInput]); + hashes[i+1] <== not_to_hash[i] * (hashes[i] - option_hash[i]) + option_hash[i]; // same as: (1 - not_to_hash[i]) * option_hash[i] + not_to_hash[i] * hash[i]; } - out <== hashes[DATA_BYTES \ 16]; } \ No newline at end of file diff --git a/package.json b/package.json index a4a479a..d3287f8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "web-prover-circuits", "description": "ZK Circuits for WebProofs", - "version": "0.5.3", + "version": "0.5.4", "license": "Apache-2.0", "repository": { "type": "git",