diff --git a/wallet-unit-poc/circom/circuits/components/age-verifier.circom b/wallet-unit-poc/circom/circuits/components/age-verifier.circom index 5ad2e23..0678af7 100644 --- a/wallet-unit-poc/circom/circuits/components/age-verifier.circom +++ b/wallet-unit-poc/circom/circuits/components/age-verifier.circom @@ -1,13 +1,13 @@ pragma circom 2.1.6; include "circomlib/circuits/comparators.circom"; +include "@zk-email/circuits/utils/array.circom"; template AgeExtractor() { signal input YYMMDD[7]; signal input currentYear; signal input currentMonth; signal input currentDay; - signal output age; signal birthROCYear <== YYMMDD[0]*100 + YYMMDD[1]*10 + YYMMDD[2]; @@ -15,98 +15,75 @@ template AgeExtractor() { signal birthMonth <== YYMMDD[3]*10 + YYMMDD[4]; signal birthDay <== YYMMDD[5]*10 + YYMMDD[6]; - // raw year difference signal rawAge <== currentYear - birthYear; - // month > birthMonth? component mGt = GreaterThan(4); mGt.in[0] <== currentMonth; mGt.in[1] <== birthMonth; - // month == birthMonth? component mEq = IsEqual(); mEq.in[0] <== currentMonth; mEq.in[1] <== birthMonth; - // currentDay ≥ birthDay? component dGe = GreaterEqThan(5); dGe.in[0] <== currentDay; dGe.in[1] <== birthDay; - // had birthday this year? signal hadBirthday; hadBirthday <== mGt.out + mEq.out * dGe.out; - - // final age = rawAge - 1 + hadBirthday age <== rawAge - 1 + hadBirthday; } +// ROC birthday format: "YYYMMDD" (7 digits, no separators) template AgeVerifier(decodedLen) { - signal input claim[decodedLen]; // ASCII codes of decoded base64: ["…","roc_birthday","0750101"] + signal input claim[decodedLen]; signal input currentYear; signal input currentMonth; signal input currentDay; - signal output ageAbove18; - + // Step 1: Find the 5th quote (opening quote of value) component isQuoteCmp[decodedLen]; - signal isQuote[decodedLen]; + signal isQuote[decodedLen]; + signal quoteCount[decodedLen]; for (var i = 0; i < decodedLen; i++) { isQuoteCmp[i] = IsEqual(); isQuoteCmp[i].in[0] <== claim[i]; isQuoteCmp[i].in[1] <== 34; - isQuote[i] <== isQuoteCmp[i].out; - } - - signal quoteCount[decodedLen]; - quoteCount[0] <== isQuote[0]; - for (var i = 1; i < decodedLen; i++) { - quoteCount[i] <== quoteCount[i-1] + isQuote[i]; + isQuote[i] <== isQuoteCmp[i].out; + quoteCount[i] <== (i == 0 ? 0 : quoteCount[i-1]) + isQuote[i]; } quoteCount[decodedLen-1] === 6; - component isThirdQuoteChar[decodedLen]; - signal isThird[decodedLen]; + // Step 2: Find index of 5th quote using one-hot encoding + // The 5th quote is where quoteCount transitions from 4 to 5 + component isFifthQuote[decodedLen]; + signal fifthQuoteIdx[decodedLen]; + signal shiftAcc[decodedLen]; for (var i = 0; i < decodedLen; i++) { - isThirdQuoteChar[i] = IsEqual(); - isThirdQuoteChar[i].in[0] <== quoteCount[i]; - isThirdQuoteChar[i].in[1] <== 5; - isThird[i] <== isThirdQuoteChar[i].out; + isFifthQuote[i] = IsEqual(); + isFifthQuote[i].in[0] <== quoteCount[i]; + isFifthQuote[i].in[1] <== 5; + // One-hot: 1 only at the exact position of the 5th quote + fifthQuoteIdx[i] <== isFifthQuote[i].out * isQuote[i]; + // Accumulate index: sum(i * oneHot[i]) gives the position + shiftAcc[i] <== (i == 0 ? 0 : shiftAcc[i-1]) + fifthQuoteIdx[i] * i; } - - - signal thirdCount[decodedLen]; - thirdCount[0] <== isThird[0]; - for (var i = 1; i < decodedLen; i++) { - thirdCount[i] <== thirdCount[i-1] + isThird[i]; - } - thirdCount[decodedLen - 1] === 8;// one opening-quote + seven digits YYMMDD + one closing-quote - - - component digitEq[7][decodedLen]; - signal matchDigit[7][decodedLen]; - signal digitAcc[7][decodedLen]; - signal birthDigits[7]; - - for (var j = 0; j < 7; j++) { - for (var i = 0; i < decodedLen; i++) { - digitEq[j][i] = IsEqual(); - digitEq[j][i].in[0] <== thirdCount[i]; - digitEq[j][i].in[1] <== j + 2; - - matchDigit[j][i] <== digitEq[j][i].out * isThird[i]; - - if (i == 0) { - digitAcc[j][0] <== matchDigit[j][0] * (claim[0] - 48); - } else { - digitAcc[j][i] <== digitAcc[j][i-1] - + matchDigit[j][i] * (claim[i] - 48); - } - } - birthDigits[j] <== digitAcc[j][decodedLen - 1]; + // shift = position of 5th quote + 1 (skip the quote itself to get first digit) + signal shift <== shiftAcc[decodedLen-1] + 1; + + // Step 3: Use VarShiftLeft to extract 7 consecutive bytes starting at the value + component shifter = VarShiftLeft(decodedLen, 7); + for (var i = 0; i < decodedLen; i++) { + shifter.in[i] <== claim[i]; } + shifter.shift <== shift; + // Step 4: Convert ASCII digits to numeric values + signal birthDigits[7]; + for (var i = 0; i < 7; i++) { + birthDigits[i] <== shifter.out[i] - 48; + } component ageExtractor = AgeExtractor(); ageExtractor.YYMMDD <== birthDigits; @@ -114,13 +91,106 @@ template AgeVerifier(decodedLen) { ageExtractor.currentMonth <== currentMonth; ageExtractor.currentDay <== currentDay; - // log("ageExtractor.age: ", ageExtractor.age); + component ageCheck = GreaterThan(8); + ageCheck.in[0] <== ageExtractor.age; + ageCheck.in[1] <== 18; + ageAbove18 <== ageCheck.out; +} - component ageAbove18Checker = GreaterThan(8); - ageAbove18Checker.in[0] <== ageExtractor.age; - ageAbove18Checker.in[1] <== 18; - ageAbove18 <== ageAbove18Checker.out; +template AgeExtractorISO() { + signal input digits[8]; // YYYYMMDD + signal input currentYear; + signal input currentMonth; + signal input currentDay; + signal output age; + + signal birthYear <== digits[0]*1000 + digits[1]*100 + digits[2]*10 + digits[3]; + signal birthMonth <== digits[4]*10 + digits[5]; + signal birthDay <== digits[6]*10 + digits[7]; + + signal rawAge <== currentYear - birthYear; + + component mGt = GreaterThan(4); + mGt.in[0] <== currentMonth; + mGt.in[1] <== birthMonth; + + component mEq = IsEqual(); + mEq.in[0] <== currentMonth; + mEq.in[1] <== birthMonth; + + component dGe = GreaterEqThan(5); + dGe.in[0] <== currentDay; + dGe.in[1] <== birthDay; - // log("ageAbove18: ", ageAbove18); + signal hadBirthday; + hadBirthday <== mGt.out + mEq.out * dGe.out; + age <== rawAge - 1 + hadBirthday; } +// ISO 8601 date format: "YYYY-MM-DD" (10 chars between quotes) +template AgeVerifierISO(decodedLen) { + signal input claim[decodedLen]; + signal input currentYear; + signal input currentMonth; + signal input currentDay; + signal output ageAbove18; + + // Step 1: Find the 5th quote + component isQuoteCmp[decodedLen]; + signal isQuote[decodedLen]; + signal quoteCount[decodedLen]; + for (var i = 0; i < decodedLen; i++) { + isQuoteCmp[i] = IsEqual(); + isQuoteCmp[i].in[0] <== claim[i]; + isQuoteCmp[i].in[1] <== 34; + isQuote[i] <== isQuoteCmp[i].out; + quoteCount[i] <== (i == 0 ? 0 : quoteCount[i-1]) + isQuote[i]; + } + quoteCount[decodedLen-1] === 6; + + // Step 2: Find index of 5th quote + component isFifthQuote[decodedLen]; + signal fifthQuoteIdx[decodedLen]; + signal shiftAcc[decodedLen]; + for (var i = 0; i < decodedLen; i++) { + isFifthQuote[i] = IsEqual(); + isFifthQuote[i].in[0] <== quoteCount[i]; + isFifthQuote[i].in[1] <== 5; + fifthQuoteIdx[i] <== isFifthQuote[i].out * isQuote[i]; + shiftAcc[i] <== (i == 0 ? 0 : shiftAcc[i-1]) + fifthQuoteIdx[i] * i; + } + // shift past the opening quote to first char: "YYYY-MM-DD" + signal shift <== shiftAcc[decodedLen-1] + 1; + + // Step 3: Use VarShiftLeft to extract 10 consecutive bytes (YYYY-MM-DD) + component shifter = VarShiftLeft(decodedLen, 10); + for (var i = 0; i < decodedLen; i++) { + shifter.in[i] <== claim[i]; + } + shifter.shift <== shift; + + // Step 4: Extract digits, skipping dashes at positions 4 and 7 + // "YYYY-MM-DD" → positions 0-3: year, 4: dash, 5-6: month, 7: dash, 8-9: day + signal birthDigits[8]; + birthDigits[0] <== shifter.out[0] - 48; + birthDigits[1] <== shifter.out[1] - 48; + birthDigits[2] <== shifter.out[2] - 48; + birthDigits[3] <== shifter.out[3] - 48; + shifter.out[4] === 45; // assert '-' + birthDigits[4] <== shifter.out[5] - 48; + birthDigits[5] <== shifter.out[6] - 48; + shifter.out[7] === 45; // assert '-' + birthDigits[6] <== shifter.out[8] - 48; + birthDigits[7] <== shifter.out[9] - 48; + + component ageExtractor = AgeExtractorISO(); + ageExtractor.digits <== birthDigits; + ageExtractor.currentYear <== currentYear; + ageExtractor.currentMonth <== currentMonth; + ageExtractor.currentDay <== currentDay; + + component ageCheck = GreaterThan(8); + ageCheck.in[0] <== ageExtractor.age; + ageCheck.in[1] <== 18; + ageAbove18 <== ageCheck.out; +} diff --git a/wallet-unit-poc/circom/circuits/ecdsa/p256/mul.circom b/wallet-unit-poc/circom/circuits/ecdsa/p256/mul.circom index 80ae4ed..ee0e15e 100644 --- a/wallet-unit-poc/circom/circuits/ecdsa/p256/mul.circom +++ b/wallet-unit-poc/circom/circuits/ecdsa/p256/mul.circom @@ -180,10 +180,10 @@ template K_add() { signal klo <== (slo + tQlo + borrow.out * (2 ** 128)) - isQuotientOne.out * qlo; signal khi <== (shi + tQhi - borrow.out * 1) - isQuotientOne.out * qhi; - component kloBits = Num2Bits(256); + component kloBits = Num2Bits(128); kloBits.in <== klo; - component khiBits = Num2Bits(256); + component khiBits = Num2Bits(128); khiBits.in <== khi; for (var i = 0; i < 128; i++) { diff --git a/wallet-unit-poc/circom/scripts/compile.sh b/wallet-unit-poc/circom/scripts/compile.sh index e86fe48..fd61990 100755 --- a/wallet-unit-poc/circom/scripts/compile.sh +++ b/wallet-unit-poc/circom/scripts/compile.sh @@ -18,7 +18,7 @@ case "$1" in jwt) npx circomkit compile jwt || { echo "Error: Failed to compile JWT."; exit 1; } cd build/jwt/ || { echo "Error: 'build/jwt/' directory not found."; exit 1; } - mv jwt.r1cs jwt_js/ || { echo "Error: Failed to move jwt.r1cs."; exit 1; } + cp jwt.r1cs jwt_js/ || { echo "Error: Failed to copy jwt.r1cs."; exit 1; } cd ../.. || exit 1 mkdir -p build/cpp || { echo "Error: Failed to create cpp directory."; exit 1; } [ ! -f build/cpp/jwt.cpp ] && cp build/jwt/jwt_cpp/jwt.cpp build/cpp/ || true @@ -28,7 +28,7 @@ case "$1" in show) npx circomkit compile show || { echo "Error: Failed to compile Show."; exit 1; } cd build/show/ || { echo "Error: 'build/show/' directory not found."; exit 1; } - mv show.r1cs show_js/ || { echo "Error: Failed to move show.r1cs."; exit 1; } + cp show.r1cs show_js/ || { echo "Error: Failed to copy show.r1cs."; exit 1; } cd ../.. || exit 1 mkdir -p build/cpp || { echo "Error: Failed to create cpp directory."; exit 1; } [ ! -f build/cpp/show.cpp ] && cp build/show/show_cpp/show.cpp build/cpp/ || true @@ -38,7 +38,7 @@ case "$1" in ecdsa) npx circomkit compile ecdsa || { echo "Error: Failed to compile ECDSA."; exit 1; } cd build/ecdsa/ || { echo "Error: 'build/ecdsa/' directory not found."; exit 1; } - mv ecdsa.r1cs ecdsa_js/ || { echo "Error: Failed to move ecdsa.r1cs."; exit 1; } + cp ecdsa.r1cs ecdsa_js/ || { echo "Error: Failed to copy ecdsa.r1cs."; exit 1; } cd ../.. || exit 1 mkdir -p build/cpp || { echo "Error: Failed to create cpp directory."; exit 1; } [ ! -f build/cpp/ecdsa.cpp ] && cp build/ecdsa/ecdsa_cpp/ecdsa.cpp build/cpp/ || true @@ -49,15 +49,15 @@ case "$1" in echo "Compiling all circuits..." mkdir -p build/cpp || { echo "Error: Failed to create cpp directory."; exit 1; } npx circomkit compile jwt || { echo "Error: Failed to compile JWT."; exit 1; } - cd build/jwt/ && mv jwt.r1cs jwt_js/ && cd ../.. || { echo "Error: Failed to process JWT."; exit 1; } + cd build/jwt/ && cp jwt.r1cs jwt_js/ && cd ../.. || { echo "Error: Failed to process JWT."; exit 1; } [ ! -f build/cpp/jwt.cpp ] && cp build/jwt/jwt_cpp/jwt.cpp build/cpp/ || true [ ! -f build/cpp/jwt.dat ] && cp build/jwt/jwt_cpp/jwt.dat build/cpp/ || true npx circomkit compile show || { echo "Error: Failed to compile Show."; exit 1; } - cd build/show/ && mv show.r1cs show_js/ && cd ../.. || { echo "Error: Failed to process Show."; exit 1; } + cd build/show/ && cp show.r1cs show_js/ && cd ../.. || { echo "Error: Failed to process Show."; exit 1; } [ ! -f build/cpp/show.cpp ] && cp build/show/show_cpp/show.cpp build/cpp/ || true [ ! -f build/cpp/show.dat ] && cp build/show/show_cpp/show.dat build/cpp/ || true npx circomkit compile ecdsa || { echo "Error: Failed to compile ECDSA."; exit 1; } - cd build/ecdsa/ && mv ecdsa.r1cs ecdsa_js/ && cd ../.. || { echo "Error: Failed to process ECDSA."; exit 1; } + cd build/ecdsa/ && cp ecdsa.r1cs ecdsa_js/ && cd ../.. || { echo "Error: Failed to process ECDSA."; exit 1; } [ ! -f build/cpp/ecdsa.cpp ] && cp build/ecdsa/ecdsa_cpp/ecdsa.cpp build/cpp/ || true [ ! -f build/cpp/ecdsa.dat ] && cp build/ecdsa/ecdsa_cpp/ecdsa.dat build/cpp/ || true echo "All circuits compiled successfully." diff --git a/wallet-unit-poc/circom/tests/circuits/age.test.ts b/wallet-unit-poc/circom/tests/circuits/age.test.ts index fd0b945..f6f5a86 100644 --- a/wallet-unit-poc/circom/tests/circuits/age.test.ts +++ b/wallet-unit-poc/circom/tests/circuits/age.test.ts @@ -2,7 +2,7 @@ import { WitnessTester } from "circomkit"; import { circomkit } from "../common"; import { assert } from "console"; -describe("AgeVerifier", () => { +describe("AgeVerifier (ROC)", () => { let circuit: WitnessTester<["claim", "currentYear", "currentMonth", "currentDay"], ["ageAbove18"]>; const maxClaimLength = 128; @@ -18,7 +18,7 @@ describe("AgeVerifier", () => { console.log("AgeVerifier constraints:", await circuit.getConstraintCount()); }); - it("should decode raw claims with padding correctly and pass constraints", async () => { + it("should decode ROC claims and pass constraints", async () => { const input = "WyJGc2w4ZWpObEFNT2Vqc1lTdjc2Z1NnIiwicm9jX2JpcnRoZGF5IiwiMTA0MDYwNSJd"; let decodedClaims = Array.from(Buffer.from(atob(input))); @@ -42,67 +42,55 @@ describe("AgeVerifier", () => { }); }); -describe("AgeExtractor", () => { - let circuit: WitnessTester<["YYMMDD", "currentYear", "currentMonth", "currentDay"], ["age"]>; +describe("AgeVerifierISO (ISO 8601)", () => { + let circuit: WitnessTester<["claim", "currentYear", "currentMonth", "currentDay"], ["ageAbove18"]>; + + const maxClaimLength = 128; + const byteLength = Math.floor((maxClaimLength * 3) / 4); before(async () => { - circuit = await circomkit.WitnessTester("AgeExtractor", { + circuit = await circomkit.WitnessTester("AgeVerifierISO", { file: "components/age-verifier", - template: "AgeExtractor", - params: [], + template: "AgeVerifierISO", + params: [byteLength], recompile: true, }); - console.log("AgeExtractor constraints:", await circuit.getConstraintCount()); + console.log("AgeVerifierISO constraints:", await circuit.getConstraintCount()); }); - function toDigits(rocYear: number, month: number, day: number): number[] { - const roc = rocYear.toString().padStart(3, "0"); - const m = month.toString().padStart(2, "0"); - const d = day.toString().padStart(2, "0"); - return [...roc, ...m, ...d].map((c) => parseInt(c, 10)); - } - - // March 15, 2025 - const currentYear = BigInt(2025); - const currentMonth = BigInt(3); - const currentDay = BigInt(15); + it("should verify age above 18 with ISO 8601 format", async () => { + const jsonStr = '["Fsl8ejNlAMOejsYSv76gSg","birthday","1968-06-05"]'; + let decodedClaims = Array.from(Buffer.from(jsonStr)); + while (decodedClaims.length < byteLength) { + decodedClaims.push(0); + } - it("calculates age when birthday has passed this year (March)", async () => { - // March 14, 2000 => ROC year = 2000 - 1911 = 89 - const YYMMDD = toDigits(89, 3, 14); const witness = await circuit.calculateWitness({ - YYMMDD, - currentYear, - currentMonth, - currentDay, + claim: decodedClaims, + currentYear: BigInt(2025), + currentMonth: BigInt(3), + currentDay: BigInt(15), }); - const signals = await circuit.readWitnessSignals(witness, ["age"]); - assert(signals.age === 25n, `Expected age 25, got ${signals.age}`); + await circuit.expectConstraintPass(witness); + const signals = await circuit.readWitnessSignals(witness, ["ageAbove18"]); + assert(signals.ageAbove18 === 1n, `Expected ageAbove18=1, got ${signals.ageAbove18}`); }); - it("calculates age when birthday is today (March)", async () => { - // March 15, 2000 => ROC year = 89 - const YYMMDD = toDigits(89, 3, 15); - const witness = await circuit.calculateWitness({ - YYMMDD, - currentYear, - currentMonth, - currentDay, - }); - const signals = await circuit.readWitnessSignals(witness, ["age"]); - assert(signals.age === 25n, `Expected age 25, got ${signals.age}`); - }); + it("should verify age below 18 with ISO 8601 format", async () => { + const jsonStr = '["Fsl8ejNlAMOejsYSv76gSg","birthday","2015-06-05"]'; + let decodedClaims = Array.from(Buffer.from(jsonStr)); + while (decodedClaims.length < byteLength) { + decodedClaims.push(0); + } - it("calculates age when birthday has not passed yet (March)", async () => { - // March 16, 2000 => ROC year = 89 - const YYMMDD = toDigits(89, 3, 16); const witness = await circuit.calculateWitness({ - YYMMDD, - currentYear, - currentMonth, - currentDay, + claim: decodedClaims, + currentYear: BigInt(2025), + currentMonth: BigInt(3), + currentDay: BigInt(15), }); - const signals = await circuit.readWitnessSignals(witness, ["age"]); - assert(signals.age === 24n, `Expected age 24, got ${signals.age}`); + await circuit.expectConstraintPass(witness); + const signals = await circuit.readWitnessSignals(witness, ["ageAbove18"]); + assert(signals.ageAbove18 === 0n, `Expected ageAbove18=0, got ${signals.ageAbove18}`); }); }); diff --git a/wallet-unit-poc/ecdsa-spartan2/Cargo.lock b/wallet-unit-poc/ecdsa-spartan2/Cargo.lock index e93d758..d4118c1 100644 --- a/wallet-unit-poc/ecdsa-spartan2/Cargo.lock +++ b/wallet-unit-poc/ecdsa-spartan2/Cargo.lock @@ -34,7 +34,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom", + "getrandom 0.2.16", "once_cell", "version_check", ] @@ -48,12 +48,211 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloy-rlp" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f70d83b765fdc080dbcd4f4db70d8d23fe4761f2f02ebfa9146b833900634b4" +dependencies = [ + "arrayvec", + "bytes", +] + [[package]] name = "anyhow" version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint 0.4.6", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint 0.4.6", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" +dependencies = [ + "ark-ff-asm 0.5.0", + "ark-ff-macros 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "arrayvec", + "digest 0.10.7", + "educe", + "itertools 0.13.0", + "num-bigint 0.4.6", + "num-traits", + "paste", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" +dependencies = [ + "quote", + "syn 2.0.110", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint 0.4.6", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint 0.4.6", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" +dependencies = [ + "num-bigint 0.4.6", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint 0.4.6", +] + +[[package]] +name = "ark-serialize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" +dependencies = [ + "ark-std 0.5.0", + "arrayvec", + "digest 0.10.7", + "num-bigint 0.4.6", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "ark-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" +dependencies = [ + "num-traits", + "rand 0.8.5", +] + [[package]] name = "arrayref" version = "0.3.9" @@ -66,6 +265,17 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "auto_impl" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -87,12 +297,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - [[package]] name = "base64" version = "0.22.1" @@ -182,6 +386,12 @@ version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +[[package]] +name = "byte-slice-cast" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" + [[package]] name = "bytecheck" version = "0.6.12" @@ -212,15 +422,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "cc" -version = "1.2.45" +version = "1.2.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" +checksum = "6354c81bbfd62d9cfa9cb3c773c2b7b2a3a482d569de977fd0e961f6e7c00583" dependencies = [ "find-msvc-tools", "shlex", @@ -235,19 +445,18 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "circom-scotia" version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9153fdc6ace2a535db93cb2cbbcf7e71335123bef95a7432c951f4330853fc7a" +source = "git+https://github.com/0xVikasRushi/circom-scotia?branch=main#d643c5d588276d86b2d0e7b9fed5defc56e40e29" dependencies = [ "anyhow", "bellpepper-core", "byteorder", "cfg-if", - "color-eyre", - "crypto-bigint", "ff", "fnv", - "getrandom", + "getrandom 0.2.16", "itertools 0.9.0", + "log", + "ruint", "serde", "serde_json", "thiserror 1.0.69", @@ -255,30 +464,23 @@ dependencies = [ ] [[package]] -name = "color-eyre" -version = "0.6.5" +name = "const_format" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" dependencies = [ - "backtrace", - "color-spantrace", - "eyre", - "indenter", - "once_cell", - "owo-colors", - "tracing-error", + "const_format_proc_macros", ] [[package]] -name = "color-spantrace" -version = "0.3.0" +name = "const_format_proc_macros" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" dependencies = [ - "once_cell", - "owo-colors", - "tracing-core", - "tracing-error", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] @@ -436,15 +638,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] -name = "crypto-bigint" -version = "0.5.5" +name = "crunchy" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "rand_core", - "serdect", - "subtle", -] +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" @@ -524,6 +721,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + [[package]] name = "digest" version = "0.10.7" @@ -555,6 +761,18 @@ dependencies = [ "witnesscalc-adapter", ] +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.110", +] + [[package]] name = "either" version = "1.15.0" @@ -581,6 +799,26 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "enum-ordinalize" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + [[package]] name = "enumset" version = "1.1.10" @@ -609,20 +847,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "eyre" -version = "0.6.12" +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fastrlp" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" dependencies = [ - "indenter", - "once_cell", + "arrayvec", + "auto_impl", + "bytes", ] [[package]] -name = "fallible-iterator" -version = "0.2.0" +name = "fastrlp" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" +checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] [[package]] name = "ff" @@ -633,7 +883,7 @@ dependencies = [ "bitvec", "byteorder", "ff_derive", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -654,15 +904,27 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.4" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand 0.8.5", + "rustc-hex", + "static_assertions", +] [[package]] name = "flate2" -version = "1.1.5" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" +checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" dependencies = [ "crc32fast", "miniz_oxide", @@ -712,6 +974,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + [[package]] name = "gimli" version = "0.26.2" @@ -736,7 +1010,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -746,7 +1020,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f82f3e7809d6118c2afbde9667b3ba5c7621723df3729cc2157fac39121ecb62" dependencies = [ - "digest", + "digest 0.10.7", "ff", "group", "halo2derive", @@ -759,7 +1033,7 @@ dependencies = [ "pairing", "paste", "plonky2_maybe_rayon", - "rand_core", + "rand_core 0.6.4", "rayon", "serde", "serde_arrays", @@ -799,9 +1073,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "hex" @@ -819,10 +1093,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] -name = "indenter" -version = "0.3.4" +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] [[package]] name = "indexmap" @@ -836,12 +1124,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", ] [[package]] @@ -853,6 +1141,24 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.14.0" @@ -910,9 +1216,9 @@ checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libm" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "lock_api" @@ -1024,7 +1330,7 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", - "rand", + "rand 0.8.5", "serde", ] @@ -1050,6 +1356,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -1067,12 +1374,6 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" -[[package]] -name = "owo-colors" -version = "4.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52" - [[package]] name = "pairing" version = "0.23.0" @@ -1082,6 +1383,34 @@ dependencies = [ "group", ] +[[package]] +name = "parity-scale-codec" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "const_format", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "rustversion", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.110", +] + [[package]] name = "parking_lot_core" version = "0.9.12" @@ -1101,6 +1430,16 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pest" +version = "2.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" +dependencies = [ + "memchr", + "ucd-trie", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -1122,6 +1461,35 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1155,6 +1523,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proptest" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" +dependencies = [ + "bitflags 2.10.0", + "num-traits", + "rand 0.9.2", + "rand_chacha 0.9.0", + "rand_xorshift", + "regex-syntax", + "unarray", +] + [[package]] name = "ptr_meta" version = "0.1.4" @@ -1184,6 +1567,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "radium" version = "0.7.0" @@ -1196,7 +1585,39 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ - "rand_core", + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", ] [[package]] @@ -1205,7 +1626,25 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core 0.9.5", ] [[package]] @@ -1289,9 +1728,9 @@ dependencies = [ [[package]] name = "rkyv" -version = "0.7.45" +version = "0.7.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" +checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1" dependencies = [ "bitvec", "bytecheck", @@ -1308,20 +1747,88 @@ dependencies = [ [[package]] name = "rkyv_derive" -version = "0.7.45" +version = "0.7.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" +checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "ruint" +version = "1.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c141e807189ad38a07276942c6623032d3753c8859c146104ac2e4d68865945a" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "ark-ff 0.5.0", + "bytes", + "fastrlp 0.3.1", + "fastrlp 0.4.0", + "num-bigint 0.4.6", + "num-integer", + "num-traits", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand 0.8.5", + "rand 0.9.2", + "rlp", + "ruint-macro", + "serde_core", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + [[package]] name = "rustc-demangle" -version = "0.1.26" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.27", +] [[package]] name = "rustversion" @@ -1349,9 +1856,18 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[package]] name = "self_cell" -version = "1.2.1" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" + +[[package]] +name = "semver" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16c2f82143577edb4921b71ede051dac62ca3c16084e918bf7b40c96ae10eb33" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] [[package]] name = "semver" @@ -1359,6 +1875,15 @@ version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +[[package]] +name = "semver-parser" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.228" @@ -1422,16 +1947,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "serdect" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" -dependencies = [ - "base16ct", - "serde", -] - [[package]] name = "sha2" version = "0.10.9" @@ -1440,7 +1955,7 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", - "digest", + "digest 0.10.7", ] [[package]] @@ -1449,7 +1964,7 @@ version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest", + "digest 0.10.7", "keccak", ] @@ -1480,9 +1995,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "simd-adler32" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "simdutf8" @@ -1512,11 +2027,11 @@ dependencies = [ "bincode", "bitvec", "byteorder", - "digest", + "digest 0.10.7", "ff", "flate2", "generic-array", - "getrandom", + "getrandom 0.2.16", "group", "halo2curves", "itertools 0.14.0", @@ -1524,12 +2039,12 @@ dependencies = [ "num-integer", "num-traits", "once_cell", - "rand_core", + "rand_core 0.6.4", "rayon", "serde", "sha3", "subtle", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "tracing-subscriber", ] @@ -1603,11 +2118,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] [[package]] @@ -1623,9 +2138,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -1687,6 +2202,36 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.23.10+spec-1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +dependencies = [ + "indexmap 2.13.0", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +dependencies = [ + "winnow", +] + [[package]] name = "tracing" version = "0.1.41" @@ -1719,16 +2264,6 @@ dependencies = [ "valuable", ] -[[package]] -name = "tracing-error" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" -dependencies = [ - "tracing", - "tracing-subscriber", -] - [[package]] name = "tracing-log" version = "0.2.0" @@ -1765,6 +2300,30 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicode-ident" version = "1.0.22" @@ -1777,11 +2336,17 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "uuid" -version = "1.18.1" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" dependencies = [ "js-sys", "wasm-bindgen", @@ -1805,6 +2370,15 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" version = "0.2.105" @@ -1957,7 +2531,7 @@ dependencies = [ "bytecheck", "enum-iterator", "enumset", - "getrandom", + "getrandom 0.2.16", "hex", "indexmap 1.9.3", "more-asserts", @@ -2003,8 +2577,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dbe55c8f9d0dbd25d9447a5a889ff90c0cc3feaa7395310d3d826b2c703eaab" dependencies = [ "bitflags 2.10.0", - "indexmap 2.12.0", - "semver", + "indexmap 2.13.0", + "semver 1.0.27", ] [[package]] @@ -2168,6 +2742,21 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + [[package]] name = "witnesscalc-adapter" version = "0.1.7" @@ -2194,3 +2783,43 @@ name = "xxhash-rust" version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + +[[package]] +name = "zerocopy" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71ddd76bcebeed25db614f82bf31a9f4222d3fbba300e6fb6c00afa26cbd4d9d" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8187381b52e32220d50b255276aa16a084ec0a9017a0ca2152a1f55c539758d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.110", +] diff --git a/wallet-unit-poc/ecdsa-spartan2/Cargo.toml b/wallet-unit-poc/ecdsa-spartan2/Cargo.toml index ed3dd1a..59ab555 100644 --- a/wallet-unit-poc/ecdsa-spartan2/Cargo.toml +++ b/wallet-unit-poc/ecdsa-spartan2/Cargo.toml @@ -12,8 +12,8 @@ name = "ecdsa-spartan2" path = "src/main.rs" [dependencies] -circom-scotia = "0.2.0" spartan2 = { git = "https://github.com/therealyingtong/Spartan2.git", branch = "zk", default-features = false } +circom-scotia = { git = "https://github.com/0xVikasRushi/circom-scotia", branch = "main" } bellpepper = "0.4.0" bellpepper-core = "0.4.0" ff = { version = "0.13.0", features = ["derive"] } diff --git a/wallet-unit-poc/ecdsa-spartan2/logs/1920_bytes b/wallet-unit-poc/ecdsa-spartan2/logs/1920_bytes index 99665a1..bb57180 100644 --- a/wallet-unit-poc/ecdsa-spartan2/logs/1920_bytes +++ b/wallet-unit-poc/ecdsa-spartan2/logs/1920_bytes @@ -3,27 +3,36 @@ ║ STARTING COMPLETE BENCHMARK PIPELINE ║ ╚════════════════════════════════════════════════╝ -✓ Prepare setup completed: 4157 ms +✓ Prepare setup completed: 4313 ms -✓ Show setup completed: 41 ms +✓ Show setup completed: 32 ms ✓ Shared blinds generated: 0 ms -✓ Prepare proof generated: 2727 ms +Generating witness for circuit jwt +Witness requires 50344268 bytes, allocating and retrying... +✓ Prepare proof generated: 1803 ms -v: (0xb7014b0117f2defa0055c6d161ed43d1d267856bf2ab5185de56e11eecf01b8d, 0x5089796bfc7629263851cf25c357a743c652828e52bbf05257cdbd72c020c2ee) +Generating witness for circuit jwt +Witness requires 50344268 bytes, allocating and retrying... +v: (0x65a29dc4cc91bb8cc02539af561579b97a0f999c16744af0be831d4f58f9e858, 0x0cb1abc76ad740ff0d41ac9f07a3385e333d504868ab63d8d2fd29790b1abe4e) new instance: Some(()) -✓ Prepare proof reblinded: 715 ms +✓ Prepare proof reblinded: 1007 ms -✓ Show proof generated: 70 ms +Generating witness for circuit show +Witness requires 384556 bytes, allocating and retrying... +✓ Show proof generated: 52 ms -v: (0xb7014b0117f2defa0055c6d161ed43d1d267856bf2ab5185de56e11eecf01b8d, 0x5089796bfc7629263851cf25c357a743c652828e52bbf05257cdbd72c020c2ee) +Generating witness for circuit show +Witness requires 384556 bytes, allocating and retrying... +v: (0x65a29dc4cc91bb8cc02539af561579b97a0f999c16744af0be831d4f58f9e858, 0x0cb1abc76ad740ff0d41ac9f07a3385e333d504868ab63d8d2fd29790b1abe4e) new instance: Some(()) -✓ Show proof reblinded: 24 ms +✓ Show proof reblinded: 29 ms -✓ Prepare proof verified: 74 ms +✓ Prepare proof verified: 76 ms ✓ Show proof verified: 9 ms + ageAbove18: false ╔════════════════════════════════════════════════╗ @@ -31,24 +40,24 @@ new instance: Some(()) ╠════════════════════════════════════════════════╣ ║ TIMING MEASUREMENTS ║ ╠════════════════════════════════════════════════╣ -║ Prepare Setup: 4157 ms ║ -║ Show Setup: 41 ms ║ +║ Prepare Setup: 4313 ms ║ +║ Show Setup: 32 ms ║ ║ Generate Blinds: 0 ms ║ -║ Prove Prepare: 2727 ms ║ -║ Reblind Prepare: 715 ms ║ -║ Prove Show: 70 ms ║ -║ Reblind Show: 24 ms ║ -║ Verify Prepare: 74 ms ║ +║ Prove Prepare: 1803 ms ║ +║ Reblind Prepare: 1007 ms ║ +║ Prove Show: 52 ms ║ +║ Reblind Show: 29 ms ║ +║ Verify Prepare: 76 ms ║ ║ Verify Show: 9 ms ║ ╠════════════════════════════════════════════════╣ ║ SIZE MEASUREMENTS ║ ╠════════════════════════════════════════════════╣ -║ Prepare Proving Key: 420.05 MB ║ -║ Prepare Verifying Key: 420.05 MB ║ -║ Show Proving Key: 3.45 MB ║ -║ Show Verifying Key: 3.45 MB ║ -║ Prepare Proof: 109.29 KB ║ -║ Show Proof: 40.41 KB ║ +║ Prepare Proving Key: 419.90 MB ║ +║ Prepare Verifying Key: 419.90 MB ║ +║ Show Proving Key: 3.37 MB ║ +║ Show Verifying Key: 3.37 MB ║ +║ Prepare Proof: 112.35 KB ║ +║ Show Proof: 40.51 KB ║ ║ Prepare Witness: 64.06 MB ║ ║ Show Witness: 512.52 KB ║ ╚════════════════════════════════════════════════╝ diff --git a/wallet-unit-poc/ecdsa-spartan2/src/circuits/prepare_circuit.rs b/wallet-unit-poc/ecdsa-spartan2/src/circuits/prepare_circuit.rs index 89db891..415931e 100644 --- a/wallet-unit-poc/ecdsa-spartan2/src/circuits/prepare_circuit.rs +++ b/wallet-unit-poc/ecdsa-spartan2/src/circuits/prepare_circuit.rs @@ -1,24 +1,30 @@ use crate::{ paths::PathConfig, prover::generate_prepare_witness, - utils::{compute_prepare_shared_scalars, PrepareSharedScalars}, + utils::{calculate_jwt_output_indices, MAX_CLAIMS_LENGTH, MAX_MATCHES}, Scalar, E, }; use bellpepper_core::{num::AllocatedNum, ConstraintSystem, SynthesisError}; use circom_scotia::{reader::load_r1cs, synthesize}; -use serde_json::Value; +use ff::Field; use spartan2::traits::circuit::SpartanCircuit; -use std::{any::type_name, fs::File, path::PathBuf}; +use std::{ + any::type_name, + path::PathBuf, + sync::{Arc, Mutex}, +}; witnesscalc_adapter::witness!(jwt); -// jwt.circom +/// PrepareCircuit wraps the JWT verification circuit. #[derive(Debug, Clone)] pub struct PrepareCircuit { /// Path configuration for resolving file paths path_config: PathConfig, /// Optional override for input JSON path input_path: Option, + /// Cached witness for reuse across synthesize and shared calls + cached_witness: Arc>>>, } impl Default for PrepareCircuit { @@ -26,6 +32,7 @@ impl Default for PrepareCircuit { Self { path_config: PathConfig::default(), input_path: None, + cached_witness: Arc::new(Mutex::new(None)), } } } @@ -36,6 +43,7 @@ impl PrepareCircuit { Self { path_config, input_path, + cached_witness: Arc::new(Mutex::new(None)), } } @@ -45,6 +53,7 @@ impl PrepareCircuit { Self { path_config: PathConfig::development(), input_path: path.into(), + cached_witness: Arc::new(Mutex::new(None)), } } @@ -60,6 +69,24 @@ impl PrepareCircuit { fn r1cs_path(&self) -> PathBuf { self.path_config.r1cs_path("jwt") } + + /// Get cached witness or generate and cache it. + fn get_or_generate_witness(&self) -> Result, SynthesisError> { + let mut cache = self.cached_witness.lock().unwrap(); + + if let Some(ref witness) = *cache { + return Ok(witness.clone()); + } + + let witness = generate_prepare_witness( + &self.path_config, + self.input_path.as_ref().map(|p| p.as_path()), + )?; + + *cache = Some(witness.clone()); + + Ok(witness) + } } impl SpartanCircuit for PrepareCircuit { @@ -78,53 +105,70 @@ impl SpartanCircuit for PrepareCircuit { let is_setup_phase = cs_type.contains("ShapeCS"); if is_setup_phase { - let r1cs = load_r1cs(&r1cs_path); + let r1cs = load_r1cs(&r1cs_path).map_err(|_| SynthesisError::AssignmentMissing)?; // Pass None for witness during setup synthesize(cs, r1cs, None)?; return Ok(()); } - // Generate witness using the dedicated function - let witness = generate_prepare_witness( - &self.path_config, - self.input_path.as_ref().map(|p| p.as_path()), - )?; + let witness = self.get_or_generate_witness()?; - let r1cs = load_r1cs(&r1cs_path); + let r1cs = load_r1cs(&r1cs_path).map_err(|_| SynthesisError::AssignmentMissing)?; synthesize(cs, r1cs, Some(witness))?; Ok(()) } fn public_values(&self) -> Result, SynthesisError> { - Ok(vec![]) + // Circom public IO: ageClaim[0..95] (96 outputs), KeyBindingX, KeyBindingY + // Witness indices 1..=98 + let layout = calculate_jwt_output_indices(MAX_MATCHES, MAX_CLAIMS_LENGTH); + let num_public = layout.age_claim_len + 2; // 96 + 2 = 98 + + let witness = self.get_or_generate_witness().ok(); + + let mut values = Vec::with_capacity(num_public); + for idx in 1..=num_public { + values.push(witness.as_ref().map(|w| w[idx]).unwrap_or(Scalar::ZERO)); + } + Ok(values) } + fn shared>( &self, cs: &mut CS, ) -> Result>, SynthesisError> { - let json_path = self.resolve_input_json(); + // Calculate witness layout + let layout = calculate_jwt_output_indices(MAX_MATCHES, MAX_CLAIMS_LENGTH); - let json_file = File::open(&json_path).map_err(|_| SynthesisError::AssignmentMissing)?; - - let json_value: Value = - serde_json::from_reader(json_file).map_err(|_| SynthesisError::AssignmentMissing)?; + // Only attempt witness generation if input path is set (skips during setup) + let witness = self + .input_path + .as_ref() + .and_then(|_| self.get_or_generate_witness().ok()); - let PrepareSharedScalars { - keybinding_x, - keybinding_y, - claim_scalars, - } = compute_prepare_shared_scalars(&json_value)?; + let keybinding_x = witness + .as_ref() + .map(|w| w[layout.keybinding_x_index]) + .unwrap_or(Scalar::ZERO); + let keybinding_y = witness + .as_ref() + .map(|w| w[layout.keybinding_y_index]) + .unwrap_or(Scalar::ZERO); let keybinding_x_alloc = AllocatedNum::alloc(cs.namespace(|| "KeyBindingX"), || Ok(keybinding_x))?; let keybinding_y_alloc = AllocatedNum::alloc(cs.namespace(|| "KeyBindingY"), || Ok(keybinding_y))?; - let mut shared_values = Vec::with_capacity(2 + claim_scalars.len()); + let mut shared_values = Vec::with_capacity(2 + layout.age_claim_len); shared_values.push(keybinding_x_alloc); shared_values.push(keybinding_y_alloc); - for (idx, claim_scalar) in claim_scalars.into_iter().enumerate() { + for idx in 0..layout.age_claim_len { + let claim_scalar = witness + .as_ref() + .map(|w| w[layout.age_claim_start + idx]) + .unwrap_or(Scalar::ZERO); let claim_alloc = AllocatedNum::alloc(cs.namespace(|| format!("Claim{idx}")), move || { Ok(claim_scalar) @@ -134,6 +178,7 @@ impl SpartanCircuit for PrepareCircuit { Ok(shared_values) } + fn precommitted>( &self, _cs: &mut CS, @@ -141,6 +186,7 @@ impl SpartanCircuit for PrepareCircuit { ) -> Result>, SynthesisError> { Ok(vec![]) } + fn num_challenges(&self) -> usize { 0 } diff --git a/wallet-unit-poc/ecdsa-spartan2/src/circuits/show_circuit.rs b/wallet-unit-poc/ecdsa-spartan2/src/circuits/show_circuit.rs index 4af74d5..e626e41 100644 --- a/wallet-unit-poc/ecdsa-spartan2/src/circuits/show_circuit.rs +++ b/wallet-unit-poc/ecdsa-spartan2/src/circuits/show_circuit.rs @@ -1,9 +1,14 @@ use crate::{paths::PathConfig, utils::*, Scalar, E}; use bellpepper_core::{num::AllocatedNum, ConstraintSystem, SynthesisError}; use circom_scotia::{reader::load_r1cs, synthesize}; -use serde_json::Value; +use ff::Field; use spartan2::traits::circuit::SpartanCircuit; -use std::{any::type_name, fs::File, path::PathBuf, time::Instant}; +use std::{ + any::type_name, + path::PathBuf, + sync::{Arc, Mutex}, + time::Instant, +}; use tracing::info; witnesscalc_adapter::witness!(show); @@ -15,6 +20,8 @@ pub struct ShowCircuit { path_config: PathConfig, /// Optional override for input JSON path input_path: Option, + /// Cached witness for reuse across synthesize and shared calls + cached_witness: Arc>>>, } impl Default for ShowCircuit { @@ -22,6 +29,7 @@ impl Default for ShowCircuit { Self { path_config: PathConfig::default(), input_path: None, + cached_witness: Arc::new(Mutex::new(None)), } } } @@ -32,6 +40,7 @@ impl ShowCircuit { Self { path_config, input_path, + cached_witness: Arc::new(Mutex::new(None)), } } @@ -41,6 +50,7 @@ impl ShowCircuit { Self { path_config: PathConfig::development(), input_path: path.into(), + cached_witness: Arc::new(Mutex::new(None)), } } @@ -57,11 +67,38 @@ impl ShowCircuit { self.path_config.r1cs_path("show") } - fn load_inputs(&self) -> Result { + /// Get cached witness or generate and cache it. + fn get_or_generate_witness(&self) -> Result, SynthesisError> { + let mut cache = self.cached_witness.lock().unwrap(); + + if let Some(ref witness) = *cache { + return Ok(witness.clone()); + } + let path = self.resolve_input_json(); info!("Loading show inputs from {}", path.display()); - let file = File::open(&path).map_err(|_| SynthesisError::AssignmentMissing)?; - serde_json::from_reader(file).map_err(|_| SynthesisError::AssignmentMissing) + + let file = std::fs::File::open(&path).map_err(|_| SynthesisError::AssignmentMissing)?; + let json_value: serde_json::Value = + serde_json::from_reader(file).map_err(|_| SynthesisError::AssignmentMissing)?; + + let inputs = parse_show_inputs(&json_value)?; + + info!("Generating witness using witnesscalc..."); + let t0 = Instant::now(); + + let inputs_json = hashmap_to_json_string(&inputs)?; + let witness_bytes = + show_witness(&inputs_json).map_err(|_| SynthesisError::Unsatisfiable)?; + + info!("witnesscalc time: {} ms", t0.elapsed().as_millis()); + + let witness = parse_witness(&witness_bytes)?; + + // Cache it + *cache = Some(witness.clone()); + + Ok(witness) } } @@ -74,10 +111,6 @@ impl SpartanCircuit for ShowCircuit { _: Option<&[Scalar]>, ) -> Result<(), SynthesisError> { let r1cs_path = self.r1cs_path(); - let json_value = self.load_inputs()?; - - // Parse inputs using declarative field definitions - let inputs = parse_show_inputs(&json_value)?; // Detect if we're in setup phase (ShapeCS) or prove phase (SatisfyingAssignment) // During setup, we only need constraint structure instead of actual witness values @@ -85,71 +118,77 @@ impl SpartanCircuit for ShowCircuit { let is_setup_phase = cs_type.contains("ShapeCS"); if is_setup_phase { - let r1cs = load_r1cs(&r1cs_path); + let r1cs = load_r1cs(&r1cs_path).map_err(|_| SynthesisError::AssignmentMissing)?; // Pass None for witness during setup synthesize(cs, r1cs, None)?; return Ok(()); } - // Generate witness using witnesscalc - info!("Generating witness using witnesscalc..."); - let t0 = Instant::now(); - - let inputs_json = hashmap_to_json_string(&inputs)?; - - // Generate raw witness bytes - let witness_bytes = - show_witness(&inputs_json).map_err(|_| SynthesisError::Unsatisfiable)?; - - info!("witnesscalc time: {} ms", t0.elapsed().as_millis()); - - // Parse witness bytes directly to Scalar - let witness = parse_witness(&witness_bytes)?; + // Use cached witness (same as shared() used) for soundness + let witness = self.get_or_generate_witness()?; - let r1cs = load_r1cs(&r1cs_path); + let r1cs = load_r1cs(&r1cs_path).map_err(|_| SynthesisError::AssignmentMissing)?; synthesize(cs, r1cs, Some(witness))?; Ok(()) } fn public_values(&self) -> Result, SynthesisError> { - Ok(vec![]) + // Circom public IO: ageAbove18 (output), deviceKeyX, deviceKeyY (inputs) + // Witness indices 1..=3 + let witness = self.get_or_generate_witness().ok(); + + let mut values = Vec::with_capacity(3); + for idx in 1..=3 { + values.push(witness.as_ref().map(|w| w[idx]).unwrap_or(Scalar::ZERO)); + } + Ok(values) } + fn shared>( &self, cs: &mut CS, ) -> Result>, SynthesisError> { - let json_value = self.load_inputs()?; + // Calculate witness layout (verified from show.sym) + let layout = calculate_show_witness_indices(MAX_CLAIMS_LENGTH); - let inputs = parse_show_inputs(&json_value)?; - let keybinding_x_bigint = inputs.get("deviceKeyX").unwrap()[0].clone(); - let keybinding_y_bigint = inputs.get("deviceKeyY").unwrap()[0].clone(); - let claim_bigints = inputs - .get("claim") - .cloned() - .ok_or(SynthesisError::AssignmentMissing)?; + // Try to get witness; use zeros if unavailable (setup phase) + // Only attempt witness generation if input path is set (skips during setup) + let witness = self + .input_path + .as_ref() + .and_then(|_| self.get_or_generate_witness().ok()); - let keybinding_x = bigint_to_scalar(keybinding_x_bigint)?; - let keybinding_y = bigint_to_scalar(keybinding_y_bigint)?; - let claim_scalars = convert_bigint_to_scalar(claim_bigints)?; + let device_key_x = witness + .as_ref() + .map(|w| w[layout.device_key_x_index]) + .unwrap_or(Scalar::ZERO); + let device_key_y = witness + .as_ref() + .map(|w| w[layout.device_key_y_index]) + .unwrap_or(Scalar::ZERO); - let kb_x = AllocatedNum::alloc(cs.namespace(|| "KeyBindingX"), || Ok(keybinding_x))?; - let kb_y = AllocatedNum::alloc(cs.namespace(|| "KeyBindingY"), || Ok(keybinding_y))?; + let kb_x = AllocatedNum::alloc(cs.namespace(|| "KeyBindingX"), || Ok(device_key_x))?; + let kb_y = AllocatedNum::alloc(cs.namespace(|| "KeyBindingY"), || Ok(device_key_y))?; - let mut shared_values = Vec::with_capacity(2 + claim_scalars.len()); + let mut shared_values = Vec::with_capacity(2 + layout.claim_len); shared_values.push(kb_x); shared_values.push(kb_y); - for (idx, claim_scalar) in claim_scalars.into_iter().enumerate() { - let claim_value = claim_scalar; + for idx in 0..layout.claim_len { + let claim_scalar = witness + .as_ref() + .map(|w| w[layout.claim_start + idx]) + .unwrap_or(Scalar::ZERO); let claim_alloc = AllocatedNum::alloc(cs.namespace(|| format!("Claim{idx}")), move || { - Ok(claim_value) + Ok(claim_scalar) })?; shared_values.push(claim_alloc); } Ok(shared_values) } + fn precommitted>( &self, _cs: &mut CS, @@ -157,6 +196,7 @@ impl SpartanCircuit for ShowCircuit { ) -> Result>, SynthesisError> { Ok(vec![]) } + fn num_challenges(&self) -> usize { 0 } diff --git a/wallet-unit-poc/ecdsa-spartan2/src/main.rs b/wallet-unit-poc/ecdsa-spartan2/src/main.rs index 5e9b0c2..5d41c62 100644 --- a/wallet-unit-poc/ecdsa-spartan2/src/main.rs +++ b/wallet-unit-poc/ecdsa-spartan2/src/main.rs @@ -29,6 +29,7 @@ use ecdsa_spartan2::{ save_keys, setup_circuit_keys, setup_circuit_keys_no_save, verify_circuit, verify_circuit_with_loaded_data, PathConfig, PrepareCircuit, ShowCircuit, E, }; +use ff::Field; use std::{env::args, fs, path::PathBuf, process, time::Instant}; use tracing::info; use tracing_subscriber::EnvFilter; @@ -280,16 +281,16 @@ fn run_complete_pipeline(input_path: Option) -> BenchmarkResults { // Step 5: Reblind Prepare info!("Step 5/9: Reblinding Prepare proof..."); // Load data before timing (file I/O should not be part of reblind benchmark) - let prepare_instance = - load_instance(path_config.artifact_path(PREPARE_INSTANCE)).expect("load prepare instance failed"); - let prepare_witness = - load_witness(path_config.artifact_path(PREPARE_WITNESS)).expect("load prepare witness failed"); - let shared_blinds = - load_shared_blinds::(path_config.artifact_path(SHARED_BLINDS)).expect("load shared_blinds failed"); + let prepare_instance = load_instance(path_config.artifact_path(PREPARE_INSTANCE)) + .expect("load prepare instance failed"); + let prepare_witness = load_witness(path_config.artifact_path(PREPARE_WITNESS)) + .expect("load prepare witness failed"); + let shared_blinds = load_shared_blinds::(path_config.artifact_path(SHARED_BLINDS)) + .expect("load shared_blinds failed"); let t0 = Instant::now(); reblind_with_loaded_data( - PrepareCircuit::default(), + PrepareCircuit::new(path_config.clone(), input_path.clone()), &prepare_pk, prepare_instance, prepare_witness, @@ -326,7 +327,7 @@ fn run_complete_pipeline(input_path: Option) -> BenchmarkResults { let t0 = Instant::now(); reblind_with_loaded_data( - ShowCircuit::default(), + ShowCircuit::new(path_config.clone(), input_path.clone()), &show_pk, show_instance, show_witness, @@ -346,7 +347,7 @@ fn run_complete_pipeline(input_path: Option) -> BenchmarkResults { // Reuse prepare_vk from setup step (already in memory) let t0 = Instant::now(); - verify_circuit_with_loaded_data(&prepare_proof, &prepare_vk); + let _prepare_public_values = verify_circuit_with_loaded_data(&prepare_proof, &prepare_vk); let verify_prepare_ms = t0.elapsed().as_millis(); println!("✓ Prepare proof verified: {} ms\n", verify_prepare_ms); @@ -358,24 +359,31 @@ fn run_complete_pipeline(input_path: Option) -> BenchmarkResults { // Reuse show_vk from setup step (already in memory) let t0 = Instant::now(); - verify_circuit_with_loaded_data(&show_proof, &show_vk); + let show_public_values = verify_circuit_with_loaded_data(&show_proof, &show_vk); let verify_show_ms = t0.elapsed().as_millis(); - println!("✓ Show proof verified: {} ms\n", verify_show_ms); + println!("✓ Show proof verified: {} ms", verify_show_ms); + if !show_public_values.is_empty() { + // println!("Show public IO: {:?}", show_public_values); + let age_above_18 = show_public_values[0] == Field::ONE; + println!(" ageAbove18: {}\n", age_above_18); + } // Measure file sizes info!("Measuring artifact sizes..."); let prepare_proving_key_bytes = get_file_size(&path_config.key_path(PREPARE_PROVING_KEY).to_string_lossy()); - let prepare_verifying_key_bytes = - get_file_size(&path_config.key_path(PREPARE_VERIFYING_KEY).to_string_lossy()); + let prepare_verifying_key_bytes = get_file_size( + &path_config + .key_path(PREPARE_VERIFYING_KEY) + .to_string_lossy(), + ); let show_proving_key_bytes = get_file_size(&path_config.key_path(SHOW_PROVING_KEY).to_string_lossy()); let show_verifying_key_bytes = get_file_size(&path_config.key_path(SHOW_VERIFYING_KEY).to_string_lossy()); let prepare_proof_bytes = get_file_size(&path_config.artifact_path(PREPARE_PROOF).to_string_lossy()); - let show_proof_bytes = - get_file_size(&path_config.artifact_path(SHOW_PROOF).to_string_lossy()); + let show_proof_bytes = get_file_size(&path_config.artifact_path(SHOW_PROOF).to_string_lossy()); let prepare_witness_bytes = get_file_size(&path_config.artifact_path(PREPARE_WITNESS).to_string_lossy()); let show_witness_bytes = @@ -436,7 +444,7 @@ fn execute_prepare(action: CircuitAction, options: CommandOptions) { } CircuitAction::Verify => { info!("Verifying Prepare proof with ZK-Spartan"); - verify_circuit( + let _public_values = verify_circuit( path_config.artifact_path(PREPARE_PROOF), path_config.key_path(PREPARE_VERIFYING_KEY), ); @@ -494,10 +502,15 @@ fn execute_show(action: CircuitAction, options: CommandOptions) { } CircuitAction::Verify => { info!("Verifying Show proof with ZK-Spartan"); - verify_circuit( + let public_values = verify_circuit( path_config.artifact_path(SHOW_PROOF), path_config.key_path(SHOW_VERIFYING_KEY), ); + // Show public IO: [ageAbove18, deviceKeyX, deviceKeyY] + if !public_values.is_empty() { + let age_above_18 = public_values[0] == Field::ONE; + println!("ageAbove18: {} (raw: {:?})", age_above_18, public_values[0]); + } } CircuitAction::Reblind => { info!("Reblind Spartan sumcheck + Hyrax PCS Show"); diff --git a/wallet-unit-poc/ecdsa-spartan2/src/prover.rs b/wallet-unit-poc/ecdsa-spartan2/src/prover.rs index d2e9f5f..801ba1a 100644 --- a/wallet-unit-poc/ecdsa-spartan2/src/prover.rs +++ b/wallet-unit-poc/ecdsa-spartan2/src/prover.rs @@ -250,25 +250,28 @@ pub fn reblind_with_loaded_data>( } } -/// Only run the verification part using ZK-Spartan -pub fn verify_circuit(proof_path: impl AsRef, vk_path: impl AsRef) { +/// Only run the verification part using ZK-Spartan. +/// Returns the public values embedded in the proof. +pub fn verify_circuit(proof_path: impl AsRef, vk_path: impl AsRef) -> Vec { let proof = load_proof(&proof_path).expect("load proof failed"); let vk = load_verifying_key(&vk_path).expect("load verifying key failed"); - verify_circuit_with_loaded_data(&proof, &vk); + verify_circuit_with_loaded_data(&proof, &vk) } -/// Verify circuit with pre-loaded data - useful for benchmarking to exclude file I/O +/// Verify circuit with pre-loaded data - useful for benchmarking to exclude file I/O. +/// Returns the public values embedded in the proof. pub fn verify_circuit_with_loaded_data( proof: &R1CSSNARK, vk: & as R1CSSNARKTrait>::VerifierKey, -) { +) -> Vec { let t0 = Instant::now(); - proof.verify(&vk).expect("verify errored"); + let public_values = proof.verify(&vk).expect("verify errored"); let verify_ms = t0.elapsed().as_millis(); info!(elapsed_ms = verify_ms, "ZK-Spartan verify"); info!("Verification successful! Time: {} ms", verify_ms); + public_values } /// Generate witness for the Prepare circuit. diff --git a/wallet-unit-poc/ecdsa-spartan2/src/utils.rs b/wallet-unit-poc/ecdsa-spartan2/src/utils.rs index c96268f..8d050b3 100644 --- a/wallet-unit-poc/ecdsa-spartan2/src/utils.rs +++ b/wallet-unit-poc/ecdsa-spartan2/src/utils.rs @@ -16,7 +16,11 @@ pub enum FieldParser { BigInt2DArray, } -/// Generic function to parse input fields from JSON based on field definitions +/// Circuit parameters matching jwt.circom instantiation in main/jwt.circom: +/// todo make it generic from circuit values +pub const MAX_MATCHES: usize = 4; +pub const MAX_CLAIMS_LENGTH: usize = 128; + pub fn parse_inputs( json_value: &Value, field_defs: &[(&str, FieldParser)], @@ -574,3 +578,45 @@ pub fn calculate_jwt_output_indices( keybinding_y_index, } } + +/// Layout information for the Show circuit signals within the witness vector. +/// Verified from build/show/show.sym: +/// witness[1] = ageAbove18 (output) +/// witness[2] = deviceKeyX (public input) +/// witness[3] = deviceKeyY (public input) +/// witness[7..7+decoded_len] = claim (private input) +#[derive(Debug, Clone, Copy)] +pub struct ShowWitnessLayout { + pub device_key_x_index: usize, + pub device_key_y_index: usize, + pub claim_start: usize, + pub claim_len: usize, +} + +impl ShowWitnessLayout { + pub fn claim_range(&self) -> Range { + self.claim_start..self.claim_start + self.claim_len + } +} + +/// Calculate witness indices for Show circuit shared values. +/// +/// Show circuit witness layout (from show.sym): +/// w[1] = ageAbove18 (output) +/// w[2] = deviceKeyX (public input) +/// w[3] = deviceKeyY (public input) +/// w[4] = currentYear (private) +/// w[5] = currentMonth (private) +/// w[6..6+decoded_len] = claim[0..decoded_len-1] (private) +/// +/// Note: Verified from show.sym - claim[0] at signal_id=7 maps to witness_index=6 +pub fn calculate_show_witness_indices(max_claims_length: usize) -> ShowWitnessLayout { + let decoded_len = (max_claims_length * 3) / 4; + + ShowWitnessLayout { + device_key_x_index: 2, + device_key_y_index: 3, + claim_start: 6, + claim_len: decoded_len, + } +}