From a61fcf0b9b16b82eb72d6b4621ced63f0ba213ba Mon Sep 17 00:00:00 2001 From: hhh_QC <52317293+cyl19970726@users.noreply.github.com> Date: Tue, 11 Jul 2023 20:12:49 +0800 Subject: [PATCH] support stark aggregation (#71) * add comment for recursive * add --agg_stage flag * add aggregation stage * add recursive_proof_to_stark.sh * add stark_aggregation.sh * add combine two zkin script * update stark_aggregation.sh * update stark_aggregation.sh * add prover.js * update stark_verifier_circom.rs * update stark aggregation shell * add prover.js * add snark_verifier.sh * add stark setup * update snark_verifier.sh * add final_test * update snark_verifier.sh * update snark_verifier.sh * update aggregation * fmt stark_verifier_circom.rs * remove unused code at main_joinzkin.js * update main_joinzkin.js * update main_joinzkin.js * update recurvie_proof_to_snark to support generate stark proof * remove recursive_proof_to_stark.sh * update stark_aggregation shell * update stark_aggregation.sh * update stark_aggregation.sh * update stark_aggregation.sh * update snark_verifier.sh: back run to generate zkey * remove recursive_proof_to_stark * fix recursive2 err after update version * update stark_aggregation shell * update stark_aggregation.sh * update stark_aggregation.sh * update main_joinzkin: generate fri proof by setup.steps * update stark_aggregation * update main_joinkin * optimize verifier circom * add snarkjs local * update main_joinzkin --- starkjs/src/recursive/main_joinzkin.js | 91 ++++++++ starky/data/c12.starkStruct.json | 7 +- starky/data/final.starkStruct.bn128.json | 13 ++ starky/data/r2.starkStruct.bn128.json | 14 ++ starky/data/r2.starkStruct.json | 13 ++ starky/src/pil2circom.rs | 1 + starky/src/stark_verifier_circom.rs | 281 ++++++++++++++++++++++- test/aggregation/package.json | 4 +- test/aggregation/test/final.test.ts | 40 ++++ test/aggregation/test/prover.js | 22 ++ test/recursive_proof_to_snark.sh | 30 ++- test/snark_verifier.sh | 106 +++++++++ test/stark_aggregation.sh | 151 ++++++++++++ test/test_aggregation_verifier.sh | 2 +- zkit/src/main.rs | 3 + zkit/src/stark.rs | 3 + 16 files changed, 768 insertions(+), 13 deletions(-) create mode 100755 starkjs/src/recursive/main_joinzkin.js create mode 100644 starky/data/final.starkStruct.bn128.json create mode 100644 starky/data/r2.starkStruct.bn128.json create mode 100644 starky/data/r2.starkStruct.json create mode 100644 test/aggregation/test/final.test.ts create mode 100644 test/aggregation/test/prover.js create mode 100755 test/snark_verifier.sh create mode 100755 test/stark_aggregation.sh diff --git a/starkjs/src/recursive/main_joinzkin.js b/starkjs/src/recursive/main_joinzkin.js new file mode 100755 index 00000000..d8529e65 --- /dev/null +++ b/starkjs/src/recursive/main_joinzkin.js @@ -0,0 +1,91 @@ +const fs= require("fs"); +const version = require("../../package").version; + +const argv = require("yargs") + .version(version) + .usage("node --starksetup --zkin1 --zkin2 --zkinout ") + .alias("s","starksetup") + .alias("1","zkin1") + .alias("2","zkin2") + .alias("o","zkinout") + .argv; + +async function run() { + + const starkSetupFile = typeof(argv.starksetup) === "string" ? argv.starksetup.trim() : "starksetup.json"; + const zkin1File = typeof(argv.zkin1) === "string" ? argv.zkin1.trim() : "zkin1.json"; + const zkin2File = typeof(argv.zkin2) === "string" ? argv.zkin2.trim() : "zkin2.json"; + const zkinOutFile = typeof(argv.zkinout) === "string" ? argv.zkinout : "zkinOut.json"; + + const starkSetup = JSON.parse(await fs.promises.readFile(starkSetupFile, "utf8")); + const zkin1 = JSON.parse(await fs.promises.readFile(zkin1File, "utf8")); + const zkin2 = JSON.parse(await fs.promises.readFile(zkin2File, "utf8")); + + const zkinOut = {}; + + zkinOut.a_publics = zkin1.publics; + zkinOut.a_rootC = zkin1.rootC; + zkinOut.a_root1 = zkin1.root1; + zkinOut.a_root2 = zkin1.root2; + zkinOut.a_root3 = zkin1.root3; + zkinOut.a_root4 = zkin1.root4; + zkinOut.a_evals = zkin1.evals; + zkinOut.a_s0_vals1 = zkin1.s0_vals1; + zkinOut.a_s0_vals3 = zkin1.s0_vals3; + zkinOut.a_s0_vals4 = zkin1.s0_vals4; + zkinOut.a_s0_valsC = zkin1.s0_valsC; + zkinOut.a_s0_siblings1 = zkin1.s0_siblings1; + zkinOut.a_s0_siblings3 = zkin1.s0_siblings3; + zkinOut.a_s0_siblings4 = zkin1.s0_siblings4; + zkinOut.a_s0_siblingsC = zkin1.s0_siblingsC; + for (let i = 1; i < starkSetup["steps"].length; i++) { + let keyRoot = `a_s${i}_root`; + let keySiblings = `a_s${i}_siblings`; + let keyVals = `a_s${i}_vals`; + + zkinOut[keyRoot] = zkin1[`s${i}_root`]; + zkinOut[keySiblings] = zkin1[`s${i}_siblings`]; + zkinOut[keyVals] = zkin1[`s${i}_vals`]; + } + zkinOut.a_finalPol = zkin1.finalPol; + + zkinOut.b_publics = zkin2.publics; + zkinOut.b_rootC = zkin2.rootC; + zkinOut.b_root1 = zkin2.root1; + zkinOut.b_root2 = zkin2.root2; + zkinOut.b_root3 = zkin2.root3; + zkinOut.b_root4 = zkin2.root4; + zkinOut.b_evals = zkin2.evals; + zkinOut.b_s0_vals1 = zkin2.s0_vals1; + zkinOut.b_s0_vals3 = zkin2.s0_vals3; + zkinOut.b_s0_vals4 = zkin2.s0_vals4; + zkinOut.b_s0_valsC = zkin2.s0_valsC; + zkinOut.b_s0_siblings1 = zkin2.s0_siblings1; + zkinOut.b_s0_siblings3 = zkin2.s0_siblings3; + zkinOut.b_s0_siblings4 = zkin2.s0_siblings4; + zkinOut.b_s0_siblingsC = zkin2.s0_siblingsC; + for (let i = 1; i < starkSetup["steps"].length; i++) { + let keyRoot = `b_s${i}_root`; + let keySiblings = `b_s${i}_siblings`; + let keyVals = `b_s${i}_vals`; + + zkinOut[keyRoot] = zkin2[`s${i}_root`]; + zkinOut[keySiblings] = zkin2[`s${i}_siblings`]; + zkinOut[keyVals] = zkin2[`s${i}_vals`]; + } + + zkinOut.b_finalPol = zkin2.finalPol; + + fs.writeFileSync(zkinOutFile, JSON.stringify(zkinOut, null, 1), "utf8"); + + console.log("file Generated Correctly"); + +} + +run().then(()=> { + process.exit(0); +}, (err) => { + console.log(err.message); + console.log(err.stack); + process.exit(1); +}); diff --git a/starky/data/c12.starkStruct.json b/starky/data/c12.starkStruct.json index c5a05450..12c4ba51 100644 --- a/starky/data/c12.starkStruct.json +++ b/starky/data/c12.starkStruct.json @@ -1,11 +1,10 @@ { - "nBits": 18, - "nBitsExt": 19, + "nBits": 15, + "nBitsExt": 16, "nQueries": 8, "verificationHashType": "GL", "steps": [ - { "nBits": 19 }, - { "nBits": 15 }, + { "nBits": 16 }, { "nBits": 11 }, { "nBits": 7 }, { "nBits": 4 } diff --git a/starky/data/final.starkStruct.bn128.json b/starky/data/final.starkStruct.bn128.json new file mode 100644 index 00000000..b2de53b9 --- /dev/null +++ b/starky/data/final.starkStruct.bn128.json @@ -0,0 +1,13 @@ +{ + "nBits": 16, + "nBitsExt": 17, + "nQueries": 8, + "verificationHashType": "BN128", + "steps": [ + { "nBits": 17 }, + { "nBits": 11 }, + { "nBits": 7 }, + { "nBits": 4 } + ] + } + \ No newline at end of file diff --git a/starky/data/r2.starkStruct.bn128.json b/starky/data/r2.starkStruct.bn128.json new file mode 100644 index 00000000..d9e58bdf --- /dev/null +++ b/starky/data/r2.starkStruct.bn128.json @@ -0,0 +1,14 @@ +{ + "nBits": 20, + "nBitsExt": 21, + "nQueries": 8, + "verificationHashType": "BN128", + "steps": [ + { "nBits": 21 }, + { "nBits": 15 }, + { "nBits": 11 }, + { "nBits": 7 }, + { "nBits": 4 } + ] + } + \ No newline at end of file diff --git a/starky/data/r2.starkStruct.json b/starky/data/r2.starkStruct.json new file mode 100644 index 00000000..4c8c6e65 --- /dev/null +++ b/starky/data/r2.starkStruct.json @@ -0,0 +1,13 @@ +{ + "nBits": 17, + "nBitsExt": 18, + "nQueries": 6, + "verificationHashType": "GL", + "steps": [ + { "nBits": 18 }, + { "nBits": 11 }, + { "nBits": 7 }, + { "nBits": 4 } + ] + } + \ No newline at end of file diff --git a/starky/src/pil2circom.rs b/starky/src/pil2circom.rs index e66dff83..809f0758 100644 --- a/starky/src/pil2circom.rs +++ b/starky/src/pil2circom.rs @@ -7,6 +7,7 @@ use crate::types::{StarkStruct, PIL}; pub struct StarkOption { pub enable_input: bool, pub verkey_input: bool, + pub agg_stage: bool, pub skip_main: bool, } diff --git a/starky/src/stark_verifier_circom.rs b/starky/src/stark_verifier_circom.rs index 2e742035..a05e58d0 100644 --- a/starky/src/stark_verifier_circom.rs +++ b/starky/src/stark_verifier_circom.rs @@ -1511,6 +1511,7 @@ template StarkVerifier() {{ }} }} }} + "#, nLastBits, 1 << nLastBits, @@ -1518,17 +1519,291 @@ template StarkVerifier() {{ 1 << nLastBits )); + /////// + // Aggregation Stage + /////// + + if options.agg_stage { + res.push_str(&format!( + r#" +template Recursive2() {{ + signal input a_publics[{}]; + signal input a_root1[4]; + signal input a_root2[4]; + signal input a_root3[4]; + signal input a_root4[4]; + + signal input b_publics[{}]; + signal input b_root1[4]; + signal input b_root2[4]; + signal input b_root3[4]; + signal input b_root4[4]; + "#, + pil.publics.len(), + pil.publics.len() + )); + + res.push_str(&format!( + r#" + signal input a_rootC[4]; + signal input b_rootC[4]; +"# + )); + + res.push_str(&format!( + r#" + signal input a_evals[{}][3]; + signal input a_s0_vals1[{}][{}]; + + signal input b_evals[{}][3]; + signal input b_s0_vals1[{}][{}]; + "#, + starkinfo.ev_map.len(), + stark_struct.nQueries, + starkinfo.map_sectionsN.get("cm1_2ns"), + starkinfo.ev_map.len(), + stark_struct.nQueries, + starkinfo.map_sectionsN.get("cm1_2ns") + )); + + if starkinfo.map_sectionsN.get("cm2_2ns") > 0 { + res.push_str(&format!( + r#" + signal input a_s0_vals2[{}][{}]; + signal input b_s0_vals2[{}][{}]; + "#, + stark_struct.nQueries, + starkinfo.map_sectionsN.get("cm2_2ns"), + stark_struct.nQueries, + starkinfo.map_sectionsN.get("cm2_2ns") + )); + } + + if starkinfo.map_sectionsN.get("cm3_2ns") > 0 { + res.push_str(&format!( + r#" + signal input a_s0_vals3[{}][{}]; + + signal input b_s0_vals3[{}][{}]; + "#, + stark_struct.nQueries, + starkinfo.map_sectionsN.get("cm3_2ns"), + stark_struct.nQueries, + starkinfo.map_sectionsN.get("cm3_2ns") + )); + } + + res.push_str(&format!( + r#" + signal input a_s0_vals4[{}][{}]; + signal input a_s0_valsC[{}][{}]; + signal input a_s0_siblings1[{}][{}][4]; + + signal input b_s0_vals4[{}][{}]; + signal input b_s0_valsC[{}][{}]; + signal input b_s0_siblings1[{}][{}][4]; + "#, + stark_struct.nQueries, + starkinfo.map_sectionsN.get("cm4_2ns"), + stark_struct.nQueries, + starkinfo.n_constants, + stark_struct.nQueries, + stark_struct.steps[0].nBits, + stark_struct.nQueries, + starkinfo.map_sectionsN.get("cm4_2ns"), + stark_struct.nQueries, + starkinfo.n_constants, + stark_struct.nQueries, + stark_struct.steps[0].nBits + )); + + if starkinfo.map_sectionsN.get("cm2_2ns") > 0 { + res.push_str(&format!( + r#" + signal input a_s0_siblings2[{}][{}][4]; + + signal input b_s0_siblings2[{}][{}][4]; + "#, + stark_struct.nQueries, + stark_struct.steps[0].nBits, + stark_struct.nQueries, + stark_struct.steps[0].nBits + )); + } + + if starkinfo.map_sectionsN.get("cm3_2ns") > 0 { + res.push_str(&format!( + r#" + signal input a_s0_siblings3[{}][{}][4]; + + signal input b_s0_siblings3[{}][{}][4]; + "#, + stark_struct.nQueries, + stark_struct.steps[0].nBits, + stark_struct.nQueries, + stark_struct.steps[0].nBits, + )); + } + + res.push_str(&format!( + r#" + signal input a_s0_siblings4[{}][{}][4]; + signal input a_s0_siblingsC[{}][{}][4]; + + signal input b_s0_siblings4[{}][{}][4]; + signal input b_s0_siblingsC[{}][{}][4]; + "#, + stark_struct.nQueries, + stark_struct.steps[0].nBits, + stark_struct.nQueries, + stark_struct.steps[0].nBits, + stark_struct.nQueries, + stark_struct.steps[0].nBits, + stark_struct.nQueries, + stark_struct.steps[0].nBits + )); + + for s in 0..(stark_struct.steps.len() - 1) { + res.push_str(&format!( + r#" + signal input a_s{}_root[4]; + + signal input b_s{}_root[4]; + "#, + s + 1, + s + 1 + )); + } + + for s in 1..stark_struct.steps.len() { + res.push_str(&format!( + r#" + signal input a_s{}_vals[{}][{}]; + signal input a_s{}_siblings[{}][{}][4]; + + signal input b_s{}_vals[{}][{}]; + signal input b_s{}_siblings[{}][{}][4]; + "#, + s, + stark_struct.nQueries, + (1 << (stark_struct.steps[s - 1].nBits - stark_struct.steps[s].nBits)) * 3, + s, + stark_struct.nQueries, + stark_struct.steps[s].nBits, + s, + stark_struct.nQueries, + (1 << (stark_struct.steps[s - 1].nBits - stark_struct.steps[s].nBits)) * 3, + s, + stark_struct.nQueries, + stark_struct.steps[s].nBits + )); + } + + res.push_str(&format!( + r#" + signal input a_finalPol[{}][3]; + + signal input b_finalPol[{}][3]; + "#, + 1 << stark_struct.steps[stark_struct.steps.len() - 1].nBits, + 1 << stark_struct.steps[stark_struct.steps.len() - 1].nBits, + )); + + res.push_str(&format!( + r#" + component vA = StarkVerifier(); + + vA.publics <== a_publics; + vA.rootC <== a_rootC; + + vA.root1 <== a_root1; + vA.root2 <== a_root2; + vA.root3 <== a_root3; + vA.root4 <== a_root4; + vA.evals <== a_evals; + vA.s0_vals1 <== a_s0_vals1; + vA.s0_vals3 <== a_s0_vals3; + vA.s0_vals4 <== a_s0_vals4; + vA.s0_valsC <== a_s0_valsC; + vA.s0_siblings1 <== a_s0_siblings1; + vA.s0_siblings3 <== a_s0_siblings3; + vA.s0_siblings4 <== a_s0_siblings4; + vA.s0_siblingsC <== a_s0_siblingsC; + + vA.finalPol <== a_finalPol; + "#, + )); + + for s in 1..(stark_struct.steps.len()) { + res.push_str(&format!( + r#" + vA.s{}_root <== a_s{}_root; + vA.s{}_vals <== a_s{}_vals; + vA.s{}_siblings <== a_s{}_siblings; + "#, + s, s, s, s, s, s, + )); + } + + res.push_str(&format!( + r#" + component vB = StarkVerifier(); + + vB.publics <== b_publics; + vB.rootC <== b_rootC; + + vB.root1 <== b_root1; + vB.root2 <== b_root2; + vB.root3 <== b_root3; + vB.root4 <== b_root4; + vB.evals <== b_evals; + vB.s0_vals1 <== b_s0_vals1; + vB.s0_vals3 <== b_s0_vals3; + vB.s0_vals4 <== b_s0_vals4; + vB.s0_valsC <== b_s0_valsC; + vB.s0_siblings1 <== b_s0_siblings1; + vB.s0_siblings3 <== b_s0_siblings3; + vB.s0_siblings4 <== b_s0_siblings4; + vB.s0_siblingsC <== b_s0_siblingsC; + + vB.finalPol <== b_finalPol; + "#, + )); + + for s in 1..(stark_struct.steps.len()) { + res.push_str(&format!( + r#" + vB.s{}_root <== b_s{}_root; + vB.s{}_vals <== b_s{}_vals; + vB.s{}_siblings <== b_s{}_siblings; + "#, + s, s, s, s, s, s, + )); + } + + res.push_str(&format!( + r#" +}} + "# + )) + } + if !options.skip_main { - if options.verkey_input { + if options.agg_stage { + res.push_str(&format!( + r#" +component main {{public [a_publics, a_rootC, b_publics,b_rootC]}}= Recursive2();"# + )); + } else if options.verkey_input { res.push_str(&format!( r#" - component main {{public [publics, rootC]}}= StarkVerifier(); +component main {{public [publics, rootC]}}= StarkVerifier(); "# )); } else { res.push_str(&format!( r#" - component main {{public [publics]}}= StarkVerifier(); +component main {{public [publics]}}= StarkVerifier(); "# )); } diff --git a/test/aggregation/package.json b/test/aggregation/package.json index 593f59d8..eb1c34ee 100644 --- a/test/aggregation/package.json +++ b/test/aggregation/package.json @@ -5,7 +5,8 @@ "clean": "rm -rf build dist typechain cache artifacts", "compile": "hardhat compile", "build": "npm run clean && npm i && ./node_modules/.bin/tsc", - "test": "npm run build && npm run compile && npx hardhat test" + "test": "npm run build && npm run compile && npx hardhat test test/agg.test.ts", + "finaltest": "npm run build && npm run compile && npx hardhat test test/final.test.ts" }, "devDependencies": { "@nomiclabs/hardhat-ethers": "^2.2.2", @@ -18,6 +19,7 @@ "ethereum-waffle": "^3.0.0", "ethers": "^5.0.31", "hardhat-gas-reporter": "^1.0.9", + "snarkjs": "^0.7.0", "ts-node": "^10.4.0", "typechain": "^8.1.1", "typescript": "^4.6.2", diff --git a/test/aggregation/test/final.test.ts b/test/aggregation/test/final.test.ts new file mode 100644 index 00000000..0489d1ff --- /dev/null +++ b/test/aggregation/test/final.test.ts @@ -0,0 +1,40 @@ +const { expect } = require("chai"); +const { ethers } = require("hardhat"); + +const proof = require("/tmp/aggregation/final_proof.json"); +const publics = require("/tmp/aggregation/final_public.json"); + +describe("Plonk verifier test", function() { + it("Groth16 Verify", async function() { + const verifierFactory = await ethers.getContractFactory("Groth16Verifier"); + const verifier = await verifierFactory.deploy(); + + await verifier.deployed(); + + const solProof = [ + [proof.pi_a[0], proof.pi_a[1]], + [ + [proof.pi_b[0][1], proof.pi_b[0][0]], + [proof.pi_b[1][1], proof.pi_b[1][0]], + ], + [proof.pi_c[0], proof.pi_c[1]], + ]; + + + expect(await verifier.verifyProof( + solProof[0], + solProof[1], + solProof[2], + publics, + )).to.equal(true); + }); + + // it("Fflonk Verify", async function() { + // const verifierFactory = await ethers.getContractFactory("FflonkVerifier"); + // const verifier = await verifierFactory.deploy(); + + // await verifier.deployed(); + + // // todo : add verify process + // }); +}); diff --git a/test/aggregation/test/prover.js b/test/aggregation/test/prover.js new file mode 100644 index 00000000..360b894b --- /dev/null +++ b/test/aggregation/test/prover.js @@ -0,0 +1,22 @@ +const snarkjs = require("snarkjs"); +require("dotenv").config(); + +async function generateG16Proof(witness) { + const result = await snarkjs.groth16.fullProve(witness, "/tmp/aggregation/circuits.wasm","/tmp/aggregation/g16.zkey"); + const inputs = result.publicSignals; + const proof = result.proof; + const solProof = [ + [proof.pi_a[0], proof.pi_a[1]], + [ + [proof.pi_b[0][1], proof.pi_b[0][0]], + [proof.pi_b[1][1], proof.pi_b[1][0]], + ], + [proof.pi_c[0], proof.pi_c[1]], + ]; + + console.log("inputs:",inputs) + + return [solProof, inputs]; +} + +exports.generateG16Proof = generateG16Proof; \ No newline at end of file diff --git a/test/recursive_proof_to_snark.sh b/test/recursive_proof_to_snark.sh index b6e439ce..cfba44b2 100755 --- a/test/recursive_proof_to_snark.sh +++ b/test/recursive_proof_to_snark.sh @@ -7,6 +7,7 @@ CIRCUIT=$3 RUNDIR="${CUR_DIR}/../starkjs" PILCACHE=$WORKSPACE/$TASK_NO/$CIRCUIT PILEXECJS=$4 +GENERATE_PROOF_TYPE=$5 mkdir -p $WORKSPACE/$TASK_NO mkdir -p $RUNDIR/circuits/$TASK_NO @@ -23,12 +24,18 @@ mkdir -p $RUNDIR/circuits && node $RUNDIR/$PILEXECJS -w $RUNDIR/circuits -i $TAS ../target/release/eigen-zkit compile -p goldilocks -i $RUNDIR/circuits/$C12_VERIFIER.circom -l $RUNDIR/node_modules/pil-stark/circuits.gl --O2=full -o $WORKSPACE/$TASK_NO +# generate the pil files and const polynomicals files +# input files : $C12_VERIFIER.r1cs $C12_VERIFIER.const $C12_VERIFIER.pil +# output files : $C12_VERIFIER.exec node $RUNDIR/src/compressor12/main_compressor12_setup.js \ -r $WORKSPACE/$C12_VERIFIER.r1cs \ -c $WORKSPACE/$C12_VERIFIER.const \ -p $WORKSPACE/$C12_VERIFIER.pil \ -e $WORKSPACE/$C12_VERIFIER.exec +# generate the commit polynomicals files +# input files : $CIRCUIT.c12.wasm $C12_VERIFIER.zkin.json $C12_VERIFIER.pil $C12_VERIFIER.exec +# output files : $C12_VERIFIER.cm node $RUNDIR/src/compressor12/main_compressor12_exec.js \ -w $WORKSPACE/$C12_VERIFIER"_js"/$CIRCUIT.c12.wasm \ -i $RUNDIR/circuits/$C12_VERIFIER.zkin.json \ @@ -38,7 +45,22 @@ node $RUNDIR/src/compressor12/main_compressor12_exec.js \ mkdir -p ./aggregation/$RECURSIVE1_VERIFIER/ -../target/release/eigen-zkit stark_prove -s ../starky/data/c12.starkStruct.bn128.json \ - -p $WORKSPACE/$C12_VERIFIER.pil.json \ - --o $WORKSPACE/$C12_VERIFIER.const \ - --m $WORKSPACE/$C12_VERIFIER.cm -c $RUNDIR/circuits/$RECURSIVE1_VERIFIER.circom --i ./aggregation/$RECURSIVE1_VERIFIER/input.json --norm_stage +if [ "$GENERATE_PROOF_TYPE" = "stark" ]; then + echo "Generate stark proof" + # generate the stark proof and the circom circuits to verify stark proof. + # input files : $C12_VERIFIER.pil.json(stark proof) $C12_VERIFIER.const(const polynomials) $C12_VERIFIER.cm (commit polynomials) + # output files : $RECURSIVE1_VERIFIER.circom $RECURSIVE1_VERIFIER/input.json + ../target/release/eigen-zkit stark_prove -s ../starky/data/c12.starkStruct.json \ + -p $WORKSPACE/$C12_VERIFIER.pil.json \ + --o $WORKSPACE/$C12_VERIFIER.const \ + --m $WORKSPACE/$C12_VERIFIER.cm -c $RUNDIR/circuits/$RECURSIVE1_VERIFIER.circom --i ./aggregation/$RECURSIVE1_VERIFIER/input.zkin.json --agg_stage --norm_stage +else + echo "Generate snark proof" + # generate the stark proof and the circom circuits to verify stark proof. + # input files : $C12_VERIFIER.pil.json(stark proof) $C12_VERIFIER.const(const polynomials) $C12_VERIFIER.cm (commit polynomials) + # output files : $RECURSIVE1_VERIFIER.circom $RECURSIVE1_VERIFIER/input.json + ../target/release/eigen-zkit stark_prove -s ../starky/data/c12.starkStruct.bn128.json \ + -p $WORKSPACE/$C12_VERIFIER.pil.json \ + --o $WORKSPACE/$C12_VERIFIER.const \ + --m $WORKSPACE/$C12_VERIFIER.cm -c $RUNDIR/circuits/$RECURSIVE1_VERIFIER.circom --i ./aggregation/$RECURSIVE1_VERIFIER/input.json --norm_stage +fi diff --git a/test/snark_verifier.sh b/test/snark_verifier.sh new file mode 100755 index 00000000..334f93c9 --- /dev/null +++ b/test/snark_verifier.sh @@ -0,0 +1,106 @@ +#!/bin/bash +set -e + +export NODE_OPTIONS="--max-old-space-size=16384" +source ~/.bashrc + +CUR_DIR=$(cd $(dirname $0);pwd) + +POWER=22 +BIG_POWER=27 +SRS=${CUR_DIR}/../keys/setup_2^${POWER}.ptau +BIG_SRS=${CUR_DIR}/../keys/setup_2^${BIG_POWER}.ptau +BIG_SRS_FINAL=${CUR_DIR}/../keys/setup_2^${BIG_POWER}.ptau + +CIRCUIT_NAME=fibonacci.final + +WORK_DIR=${CUR_DIR}/aggregation/$CIRCUIT_NAME + +SNARK_CIRCOM=$WORK_DIR/$CIRCUIT_NAME.circom +SNARK_INPUT=$WORK_DIR/final_input.zkin.json + +RUNDIR="${CUR_DIR}/../starkjs" + +SNARKJS=${CUR_DIR}/aggregation/node_modules/snarkjs/build/cli.cjs +if [ ! -d "${CUR_DIR}/aggregation/node_modules/snarkjs" ]; then + cd ${CUR_DIR}/aggregation && npm install +fi + +ZKIT="${CUR_DIR}/../target/release/eigen-zkit" + +snark_type=${1-groth16} +first_run=${2-false} + +if [ $first_run = "true" ]; then + echo "compile circom and generate wasm and r1cs" + circom $CUR_DIR/../starkjs/circuits/$CIRCUIT_NAME.circom --wasm --r1cs -p bn128 -l "../starkjs/node_modules/pil-stark/circuits.bn128" -l "../starkjs/node_modules/circomlib/circuits" --O2=full -o $WORK_DIR + # cp $WORK_DIR/$CIRCUIT_NAME"_js"/$CIRCUIT_NAME.wasm /tmp/aggregation/circuits.wasm +fi + + +if [ $snark_type = "groth16" ]; then + if [ ! -f $SRS ]; then + echo "downloading powersOfTau28_hez_final_${POWER}.ptau" + curl https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_${POWER}.ptau -o $SRS + fi + + echo ">>> groth16 scheme <<< " + if [ "$2" = "true" ]; then + echo "1. generate groth16 zkey" + $SNARKJS g16s $WORK_DIR/$CIRCUIT_NAME.r1cs $SRS $WORK_DIR/g16.zkey + else + echo "1. groth16 zkey already generated" + fi + + echo "2. groth16 fullprove" + $SNARKJS g16f $SNARK_INPUT $WORK_DIR/$CIRCUIT_NAME"_js"/$CIRCUIT_NAME.wasm $WORK_DIR/g16.zkey $WORK_DIR/proof.json $WORK_DIR/public.json + + if [ $first_run = "true" ]; then + echo "3. generate verification_key" + $SNARKJS zkev $WORK_DIR/g16.zkey $WORK_DIR/verification_key.json + + echo "4. verify groth16 proof" + $SNARKJS g16v $WORK_DIR/verification_key.json $WORK_DIR/public.json $WORK_DIR/proof.json + + cp $WORK_DIR/public.json /tmp/aggregation/final_public.json + cp $WORK_DIR/proof.json /tmp/aggregation/final_proof.json + + echo "5. generate verifier contract" + $SNARKJS zkesv $WORK_DIR/g16.zkey ${CUR_DIR}/aggregation/contracts/final_verifier.sol + + echo "6. calculate verify gas cost" + cd aggregation && npx hardhat test test/final.test.ts + fi + +else + if [ ! -f $SRS ]; then + echo "downloading powersOfTau28_hez_final_${POWER}.ptau" + curl https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_${POWER}.ptau -o $BIG_SRS + fi + + echo ">>> fflonk scheme <<< " + echo "1. fflonk setup " + + if [ ! -f "$WORK_DIR/fflonk.zkey" ]; then + echo "1. generate groth16 zkey" + # nohup snarkjs ffs $WORK_DIR/$CIRCUIT_NAME.r1cs $BIG_SRS $WORK_DIR/fflonk.zkey & + $SNARKJS ffs $WORK_DIR/$CIRCUIT_NAME.r1cs $BIG_SRS $WORK_DIR/fflonk.zkey + else + echo "1. fflonk zkey already generated" + fi + + echo "2. fflonk fullprove" + $SNARKJS ffs $WORK_DIR/$CIRCUIT_NAME.r1cs $BIG_SRS_FINAL $WORK_DIR/fflonk.zkey + $SNARKJS pkf $SNARK_INPUT $WORK_DIR/$CIRCUIT_NAME"_js"/$CIRCUIT_NAME.wasm $WORK_DIR/fflonk.zkey $WORK_DIR/proof.fflonk.json $WORK_DIR/public.fflonk. + echo "3. generate verification_key" + $SNARKJS zkev $WORK_DIR/fflonk.zkey $WORK_DIR/verification_key.fflonk.json + + echo "4. verify fflonk proof" + $SNARKJS ffv $WORK_DIR/verification_key.fflonk.json $WORK_DIR/public.fflonk.json $WORK_DIR/proof.fflonk.json + + cp $WORK_DIR/public.fflonk.json /tmp/aggregation/final_public.fflonk.json + cp $WORK_DIR/proof.fflonk.json /tmp/aggregation/final_proof.fflonk.json + + echo "5. generate verifier contract" + $SNARKJS zkesv $WORK_DIR/fflonk.zkey ${CUR_DIR}/aggregation/contracts/final_verifier_fflonk.sol +fi \ No newline at end of file diff --git a/test/stark_aggregation.sh b/test/stark_aggregation.sh new file mode 100755 index 00000000..062db349 --- /dev/null +++ b/test/stark_aggregation.sh @@ -0,0 +1,151 @@ +#!/bin/bash +set -e + +## build +cargo build --release + +export NODE_OPTIONS="--max-old-space-size=81920" +source ~/.bashrc + +BIG_POWER=26 +NUM_PROOF=2 +NUM_INPUT=2 +CUR_DIR=$(cd $(dirname $0);pwd) +ZKIT="${CUR_DIR}/../target/release/eigen-zkit" +CIRCUIT="fibonacci" +PILEXECJS="fibonacci/fibonacci.js" +RUNDIR="${CUR_DIR}/../starkjs" + +WORKSPACE=/tmp/aggregation_$CIRCUIT + +first_run=${1-no} +if [ $first_run = "yes" ]; then + rm -rf $WORKSPACE && mkdir -p $WORKSPACE +fi + +RECURSIVE_CIRCUIT=$CIRCUIT.recursive1 +RECURSIVE2_CIRCUIT=$CIRCUIT.recursive2 +FINAL_CIRCUIT=$CIRCUIT.final + +input0=$CUR_DIR/aggregation/0/${RECURSIVE_CIRCUIT} && mkdir -p $input0 +input1=$CUR_DIR/aggregation/1/${RECURSIVE_CIRCUIT} && mkdir -p $input1 + +mkdir -p ./aggregation/$RECURSIVE2_CIRCUIT +mkdir -p ./aggregation/$FINAL_CIRCUIT + +# test poseidon +#CIRCUIT="poseidon" +#PILEXECJS="poseidon/main_poseidon.js" + +c12_start=$(date +%s) +cd ${CUR_DIR} && npm i +for (( i=0; i<$NUM_PROOF; i++ )) +do + ./recursive_proof_to_snark.sh $i $WORKSPACE $CIRCUIT $PILEXECJS "stark" +done +c12_end=$(date +%s) + + +aggregation_start=$(date +%s) + +echo " ==> aggregation stage <== " +if [ ! -f "$WORKSPACE/$RECURSIVE_CIRCUIT.r1cs" ]; then + echo "1. compile circuit, use task 0 by default" + ${ZKIT} compile -p goldilocks -i $CUR_DIR/../starkjs/circuits/0/$RECURSIVE_CIRCUIT.circom -l "../starkjs/node_modules/pil-stark/circuits.gl" -l "../starkjs/node_modules/circomlib/circuits" --O2=full -o $WORKSPACE +else + echo "1.no need compile circom : "$WORKSPACE/$RECURSIVE_CIRCUIT.r1cs" already generated" +fi + + +echo "2. combine input1.zkin.json with input2.zkin.json " +node $RUNDIR/src/recursive/main_joinzkin.js --starksetup ../starky/data/c12.starkStruct.json --zkin1 $input0/input.zkin.json --zkin2 $input1/input.zkin.json --zkinout $input0/r1_input.zkin.json + +echo "3. generate the pil files and const polynomicals files " +# generate the pil files and const polynomicals files +# input files : $C12_VERIFIER.r1cs +# output files : $C12_VERIFIER.const $C12_VERIFIER.pil $C12_VERIFIER.exec +if [ ! -f "$WORKSPACE/$RECURSIVE_CIRCUIT.pil" ]; then + node $RUNDIR/src/compressor12/main_compressor12_setup.js \ + -r $WORKSPACE/$RECURSIVE_CIRCUIT.r1cs \ + -c $WORKSPACE/$RECURSIVE_CIRCUIT.const \ + -p $WORKSPACE/$RECURSIVE_CIRCUIT.pil \ + -e $WORKSPACE/$RECURSIVE_CIRCUIT.exec +fi + +echo "4. generate the commit polynomicals files " +# generate the commit polynomicals files +# input files : $CIRCUIT.c12.wasm $C12_VERIFIER.zkin.json $C12_VERIFIER.pil /$C12_VERIFIER.exec +# output files : $C12_VERIFIER.cm +node $RUNDIR/src/compressor12/main_compressor12_exec.js \ + -w $WORKSPACE/$RECURSIVE_CIRCUIT"_js"/$RECURSIVE_CIRCUIT.wasm \ + -i $input0/r1_input.zkin.json \ + -p $WORKSPACE/$RECURSIVE_CIRCUIT.pil \ + -e $WORKSPACE/$RECURSIVE_CIRCUIT.exec \ + -m $WORKSPACE/$RECURSIVE_CIRCUIT.cm + +echo "5. generate recursive2 proof " +# generate the stark proof and the circom circuits to verify stark proof. +# input files : $C12_VERIFIER.pil.json(stark proof) $C12_VERIFIER.const(const polynomials) $C12_VERIFIER.cm (commit polynomials) +# output files : $RECURSIVE2_CIRCUIT.circom $RECURSIVE2_CIRCUIT/r2_input.json +# Remark: the N of r2.starkStruct must be 2^20 , because the degree of $RECURSIVE_CIRCUIT.pil is 2^20 which determined by the proocess of converting $RECURSIVE_CIRCUIT.circom to $RECURSIVE_CIRCUIT.pil +../target/release/eigen-zkit stark_prove -s ../starky/data/r2.starkStruct.json \ + -p $WORKSPACE/$RECURSIVE_CIRCUIT.pil.json \ + --o $WORKSPACE/$RECURSIVE_CIRCUIT.const \ + --m $WORKSPACE/$RECURSIVE_CIRCUIT.cm -c $RUNDIR/circuits/$RECURSIVE2_CIRCUIT.circom --i ./aggregation/$RECURSIVE2_CIRCUIT/r2_input.zkin.json --norm_stage + +aggregation_end=$(date +%s) + + +final_start=$(date +%s) +# final recursive stage +echo " ==> final recursive stage <== " +if [ ! -f "$WORKSPACE/$RECURSIVE2_CIRCUIT.r1cs" ]; then + echo "1. compile circuit and generate r1cs and wasm" + ${ZKIT} compile -p goldilocks -i $RUNDIR/circuits/$RECURSIVE2_CIRCUIT.circom -l "../starkjs/node_modules/pil-stark/circuits.gl" -l "../starkjs/node_modules/circomlib/circuits" --O2=full -o $WORKSPACE +else + echo "1.no need compile circom : "$WORKSPACE/$RECURSIVE2_CIRCUIT.r1cs" already generated" +fi + + +echo "2. generate the pil files and const polynomicals files " +if [ ! -f "$WORKSPACE/$RECURSIVE2_CIRCUIT.pil" ]; then + node $RUNDIR/src/compressor12/main_compressor12_setup.js \ + -r $WORKSPACE/$RECURSIVE2_CIRCUIT.r1cs \ + -c $WORKSPACE/$RECURSIVE2_CIRCUIT.const \ + -p $WORKSPACE/$RECURSIVE2_CIRCUIT.pil \ + -e $WORKSPACE/$RECURSIVE2_CIRCUIT.exec +fi + +echo "3. generate the commit polynomicals files " +node $RUNDIR/src/compressor12/main_compressor12_exec.js \ + -w $WORKSPACE/$RECURSIVE2_CIRCUIT"_js"/$RECURSIVE2_CIRCUIT.wasm \ + -i ./aggregation/$RECURSIVE2_CIRCUIT/r2_input.zkin.json \ + -p $WORKSPACE/$RECURSIVE2_CIRCUIT.pil \ + -e $WORKSPACE/$RECURSIVE2_CIRCUIT.exec \ + -m $WORKSPACE/$RECURSIVE2_CIRCUIT.cm + + +echo "4. generate final proof " +# Remark: the N of final.starkStruct must be 2^20 , because the degree of $RECURSIVE2_CIRCUIT.pil is 2^20 which determined by the proocess of converting $RECURSIVE_CIRCUIT2.circom to $RECURSIVE_CIRCUIT2.pil +../target/release/eigen-zkit stark_prove -s ../starky/data/final.starkStruct.bn128.json \ + -p $WORKSPACE/$RECURSIVE2_CIRCUIT.pil.json \ + --o $WORKSPACE/$RECURSIVE2_CIRCUIT.const \ + --m $WORKSPACE/$RECURSIVE2_CIRCUIT.cm -c $RUNDIR/circuits/$FINAL_CIRCUIT.circom --i ./aggregation/$FINAL_CIRCUIT/final_input.zkin.json --norm_stage + +final_end=$(date +%s) + +snark_start=$(date +%s) + +if [ $first_run = "yes" ]; then + ./snark_verifier.sh groth16 true +else + ./snark_verifier.sh groth16 false +fi + +snark_end=$(date +%s) + +echo "C12 Stage Time Cost ($((c12_end - c12_start))s)" +echo "Aggregation Stage Time Cost ($((aggregation_end - aggregation_start))s)" +echo "Final Stage Time Cost ($((final_end - final_start))s)" +echo "Recursive Snark Stage Time Cost ($((snark_end - snark_start))s)" +echo "Full Process Time Cost ($((snark_end - c12_start))s)" diff --git a/test/test_aggregation_verifier.sh b/test/test_aggregation_verifier.sh index d6be0c20..6e1a06d6 100755 --- a/test/test_aggregation_verifier.sh +++ b/test/test_aggregation_verifier.sh @@ -23,7 +23,7 @@ rm -rf $WORKSPACE && mkdir -p $WORKSPACE cd ${CUR_DIR} && npm i for (( i=0; i<$NUM_PROOF; i++ )) do - nohup ./recursive_proof_to_snark.sh $i $WORKSPACE $CIRCUIT $PILEXECJS & + nohup ./recursive_proof_to_snark.sh $i $WORKSPACE $CIRCUIT $PILEXECJS "snark" & done wait diff --git a/zkit/src/main.rs b/zkit/src/main.rs index 014725c8..5135e214 100644 --- a/zkit/src/main.rs +++ b/zkit/src/main.rs @@ -214,6 +214,8 @@ struct StarkProveOpt { piljson: String, #[arg(short, long = "norm_stage", action= clap::ArgAction::SetTrue)] norm_stage: bool, + #[arg(short, long = "agg_stage", action= clap::ArgAction::SetTrue)] + agg_stage: bool, #[arg(long = "o", default_value = "pols.const")] const_pols: String, #[arg(long = "m", default_value = "pols.cm")] @@ -390,6 +392,7 @@ fn main() { &args.stark_struct, &args.piljson, args.norm_stage, + args.agg_stage, &args.const_pols, &args.cm_pols, &args.circom_file, diff --git a/zkit/src/stark.rs b/zkit/src/stark.rs index 42d35387..0c5276d3 100644 --- a/zkit/src/stark.rs +++ b/zkit/src/stark.rs @@ -18,6 +18,7 @@ pub fn prove( stark_struct: &String, pil_file: &String, norm_stage: bool, + agg_stage: bool, const_pol_file: &String, cm_pol_file: &String, circom_file: &String, @@ -62,6 +63,7 @@ pub fn prove( enable_input: false, verkey_input: norm_stage, skip_main: false, + agg_stage: false, }; println!("generate circom"); @@ -116,6 +118,7 @@ pub fn prove( enable_input: false, verkey_input: norm_stage, skip_main: false, + agg_stage: agg_stage, }; println!("generate circom");