|
| 1 | +/* eslint-disable no-plusplus */ |
| 2 | +/* eslint-disable eqeqeq */ |
| 3 | +/* eslint-disable @typescript-eslint/naming-convention */ |
| 4 | +/* istanbul ignore file */ |
| 5 | + |
| 6 | +// @ts-ignore |
| 7 | +import * as binFileUtils from "@iden3/binfileutils" |
| 8 | +import { BigBuffer, Scalar, utils } from "ffjavascript" |
| 9 | +import { log2 } from "./utils" |
| 10 | +import * as zkeyUtils from "./zkey-utils" |
| 11 | +import * as wtnsUtils from "./wtns-utils" |
| 12 | +import wtnsCalculate from "./wtns-calculate" |
| 13 | + |
| 14 | +const { stringifyBigInts, unstringifyBigInts } = utils |
| 15 | + |
| 16 | +async function buildABC1(curve: any, zkey: any, witness: any, coeffs: any) { |
| 17 | + const { n8 } = curve.Fr |
| 18 | + const sCoef = 4 * 3 + zkey.n8r |
| 19 | + const nCoef = (coeffs.byteLength - 4) / sCoef |
| 20 | + |
| 21 | + const outBuffA = new BigBuffer(zkey.domainSize * n8) |
| 22 | + const outBuffB = new BigBuffer(zkey.domainSize * n8) |
| 23 | + const outBuffC = new BigBuffer(zkey.domainSize * n8) |
| 24 | + |
| 25 | + const outBuf = [outBuffA, outBuffB] |
| 26 | + for (let i = 0; i < nCoef; i++) { |
| 27 | + const buffCoef = coeffs.slice(4 + i * sCoef, 4 + i * sCoef + sCoef) |
| 28 | + const buffCoefV = new DataView(buffCoef.buffer) |
| 29 | + const m = buffCoefV.getUint32(0, true) |
| 30 | + const c = buffCoefV.getUint32(4, true) |
| 31 | + const s = buffCoefV.getUint32(8, true) |
| 32 | + const coef = buffCoef.slice(12, 12 + n8) |
| 33 | + outBuf[m].set( |
| 34 | + curve.Fr.add(outBuf[m].slice(c * n8, c * n8 + n8), curve.Fr.mul(coef, witness.slice(s * n8, s * n8 + n8))), |
| 35 | + c * n8 |
| 36 | + ) |
| 37 | + } |
| 38 | + |
| 39 | + for (let i = 0; i < zkey.domainSize; i++) { |
| 40 | + outBuffC.set(curve.Fr.mul(outBuffA.slice(i * n8, i * n8 + n8), outBuffB.slice(i * n8, i * n8 + n8)), i * n8) |
| 41 | + } |
| 42 | + |
| 43 | + return [outBuffA, outBuffB, outBuffC] |
| 44 | +} |
| 45 | + |
| 46 | +async function joinABC(curve: any, _zkey: any, a: any, b: any, c: any) { |
| 47 | + const MAX_CHUNK_SIZE = 1 << 22 |
| 48 | + |
| 49 | + const { n8 } = curve.Fr |
| 50 | + const nElements = Math.floor(a.byteLength / curve.Fr.n8) |
| 51 | + |
| 52 | + const promises = [] |
| 53 | + |
| 54 | + for (let i = 0; i < nElements; i += MAX_CHUNK_SIZE) { |
| 55 | + const n = Math.min(nElements - i, MAX_CHUNK_SIZE) |
| 56 | + |
| 57 | + const task = [] |
| 58 | + |
| 59 | + const aChunk = a.slice(i * n8, (i + n) * n8) |
| 60 | + const bChunk = b.slice(i * n8, (i + n) * n8) |
| 61 | + const cChunk = c.slice(i * n8, (i + n) * n8) |
| 62 | + |
| 63 | + task.push({ cmd: "ALLOCSET", var: 0, buff: aChunk }) |
| 64 | + task.push({ cmd: "ALLOCSET", var: 1, buff: bChunk }) |
| 65 | + task.push({ cmd: "ALLOCSET", var: 2, buff: cChunk }) |
| 66 | + task.push({ cmd: "ALLOC", var: 3, len: n * n8 }) |
| 67 | + task.push({ |
| 68 | + cmd: "CALL", |
| 69 | + fnName: "qap_joinABC", |
| 70 | + params: [{ var: 0 }, { var: 1 }, { var: 2 }, { val: n }, { var: 3 }] |
| 71 | + }) |
| 72 | + task.push({ cmd: "CALL", fnName: "frm_batchFromMontgomery", params: [{ var: 3 }, { val: n }, { var: 3 }] }) |
| 73 | + task.push({ cmd: "GET", out: 0, var: 3, len: n * n8 }) |
| 74 | + promises.push(curve.tm.queueAction(task)) |
| 75 | + } |
| 76 | + |
| 77 | + const result = await Promise.all(promises) |
| 78 | + |
| 79 | + let outBuff |
| 80 | + if (a instanceof BigBuffer) { |
| 81 | + // @ts-ignore |
| 82 | + outBuff = new BigBuffer(a.byteLength) |
| 83 | + } else { |
| 84 | + outBuff = new Uint8Array(a.byteLength) |
| 85 | + } |
| 86 | + |
| 87 | + let p = 0 |
| 88 | + for (let i = 0; i < result.length; i++) { |
| 89 | + outBuff.set(result[i][0], p) |
| 90 | + p += result[i][0].byteLength |
| 91 | + } |
| 92 | + |
| 93 | + return outBuff |
| 94 | +} |
| 95 | + |
| 96 | +export default async function groth16Prove(_input: any, wasmFile: any, zkeyFileName: any) { |
| 97 | + const input = unstringifyBigInts(_input) |
| 98 | + |
| 99 | + const witnessFileName = { |
| 100 | + type: "mem" |
| 101 | + } |
| 102 | + |
| 103 | + await wtnsCalculate(input, wasmFile, witnessFileName) |
| 104 | + |
| 105 | + const { fd: fdWtns, sections: sectionsWtns } = await binFileUtils.readBinFile( |
| 106 | + witnessFileName, |
| 107 | + "wtns", |
| 108 | + 2, |
| 109 | + 1 << 25, |
| 110 | + 1 << 23 |
| 111 | + ) |
| 112 | + |
| 113 | + const wtns = await wtnsUtils.readHeader(fdWtns, sectionsWtns) |
| 114 | + |
| 115 | + const { fd: fdZKey, sections: sectionsZKey } = await binFileUtils.readBinFile( |
| 116 | + zkeyFileName, |
| 117 | + "zkey", |
| 118 | + 2, |
| 119 | + 1 << 25, |
| 120 | + 1 << 23 |
| 121 | + ) |
| 122 | + |
| 123 | + const zkey = await zkeyUtils.readHeader(fdZKey, sectionsZKey, undefined) |
| 124 | + |
| 125 | + if (zkey.protocol !== "groth16") { |
| 126 | + throw new Error("zkey file is not groth16") |
| 127 | + } |
| 128 | + |
| 129 | + if (!Scalar.eq(zkey.r, wtns.q)) { |
| 130 | + throw new Error("Curve of the witness does not match the curve of the proving key") |
| 131 | + } |
| 132 | + |
| 133 | + if (wtns.nWitness !== zkey.nVars) { |
| 134 | + throw new Error(`Invalid witness length. Circuit: ${zkey.nVars}, witness: ${wtns.nWitness}`) |
| 135 | + } |
| 136 | + |
| 137 | + const { curve } = zkey |
| 138 | + const { Fr } = curve |
| 139 | + const { G1 } = curve |
| 140 | + const { G2 } = curve |
| 141 | + |
| 142 | + const power = log2(zkey.domainSize) |
| 143 | + |
| 144 | + const buffWitness = await binFileUtils.readSection(fdWtns, sectionsWtns, 2) |
| 145 | + const buffCoeffs = await binFileUtils.readSection(fdZKey, sectionsZKey, 4) |
| 146 | + |
| 147 | + const [buffA_T, buffB_T, buffC_T] = await buildABC1(curve, zkey, buffWitness, buffCoeffs) |
| 148 | + |
| 149 | + const inc = power == Fr.s ? curve.Fr.shift : curve.Fr.w[power + 1] |
| 150 | + |
| 151 | + const buffA = await Fr.ifft(buffA_T, "", "", undefined, "IFFT_A") |
| 152 | + const buffAodd = await Fr.batchApplyKey(buffA, Fr.e(1), inc) |
| 153 | + const buffAodd_T = await Fr.fft(buffAodd, "", "", undefined, "FFT_A") |
| 154 | + |
| 155 | + const buffB = await Fr.ifft(buffB_T, "", "", undefined, "IFFT_B") |
| 156 | + const buffBodd = await Fr.batchApplyKey(buffB, Fr.e(1), inc) |
| 157 | + const buffBodd_T = await Fr.fft(buffBodd, "", "", undefined, "FFT_B") |
| 158 | + |
| 159 | + const buffC = await Fr.ifft(buffC_T, "", "", undefined, "IFFT_C") |
| 160 | + const buffCodd = await Fr.batchApplyKey(buffC, Fr.e(1), inc) |
| 161 | + const buffCodd_T = await Fr.fft(buffCodd, "", "", undefined, "FFT_C") |
| 162 | + |
| 163 | + const buffPodd_T = await joinABC(curve, zkey, buffAodd_T, buffBodd_T, buffCodd_T) |
| 164 | + |
| 165 | + let proof: any = {} |
| 166 | + |
| 167 | + const buffBasesA = await binFileUtils.readSection(fdZKey, sectionsZKey, 5) |
| 168 | + proof.pi_a = await curve.G1.multiExpAffine(buffBasesA, buffWitness, undefined, "multiexp A") |
| 169 | + |
| 170 | + const buffBasesB1 = await binFileUtils.readSection(fdZKey, sectionsZKey, 6) |
| 171 | + let pib1 = await curve.G1.multiExpAffine(buffBasesB1, buffWitness, undefined, "multiexp B1") |
| 172 | + |
| 173 | + const buffBasesB2 = await binFileUtils.readSection(fdZKey, sectionsZKey, 7) |
| 174 | + proof.pi_b = await curve.G2.multiExpAffine(buffBasesB2, buffWitness, undefined, "multiexp B2") |
| 175 | + |
| 176 | + const buffBasesC = await binFileUtils.readSection(fdZKey, sectionsZKey, 8) |
| 177 | + proof.pi_c = await curve.G1.multiExpAffine( |
| 178 | + buffBasesC, |
| 179 | + buffWitness.slice((zkey.nPublic + 1) * curve.Fr.n8), |
| 180 | + undefined, |
| 181 | + "multiexp C" |
| 182 | + ) |
| 183 | + |
| 184 | + const buffBasesH = await binFileUtils.readSection(fdZKey, sectionsZKey, 9) |
| 185 | + const resH = await curve.G1.multiExpAffine(buffBasesH, buffPodd_T, undefined, "multiexp H") |
| 186 | + |
| 187 | + const r = curve.Fr.random() |
| 188 | + const s = curve.Fr.random() |
| 189 | + |
| 190 | + proof.pi_a = G1.add(proof.pi_a, zkey.vk_alpha_1) |
| 191 | + proof.pi_a = G1.add(proof.pi_a, G1.timesFr(zkey.vk_delta_1, r)) |
| 192 | + |
| 193 | + proof.pi_b = G2.add(proof.pi_b, zkey.vk_beta_2) |
| 194 | + proof.pi_b = G2.add(proof.pi_b, G2.timesFr(zkey.vk_delta_2, s)) |
| 195 | + |
| 196 | + pib1 = G1.add(pib1, zkey.vk_beta_1) |
| 197 | + pib1 = G1.add(pib1, G1.timesFr(zkey.vk_delta_1, s)) |
| 198 | + |
| 199 | + proof.pi_c = G1.add(proof.pi_c, resH) |
| 200 | + |
| 201 | + proof.pi_c = G1.add(proof.pi_c, G1.timesFr(proof.pi_a, s)) |
| 202 | + proof.pi_c = G1.add(proof.pi_c, G1.timesFr(pib1, r)) |
| 203 | + proof.pi_c = G1.add(proof.pi_c, G1.timesFr(zkey.vk_delta_1, Fr.neg(Fr.mul(r, s)))) |
| 204 | + |
| 205 | + let publicSignals = [] |
| 206 | + |
| 207 | + for (let i = 1; i <= zkey.nPublic; i++) { |
| 208 | + const b = buffWitness.slice(i * Fr.n8, i * Fr.n8 + Fr.n8) |
| 209 | + publicSignals.push(Scalar.fromRprLE(b, undefined, undefined)) |
| 210 | + } |
| 211 | + |
| 212 | + proof.pi_a = G1.toObject(G1.toAffine(proof.pi_a)) |
| 213 | + proof.pi_b = G2.toObject(G2.toAffine(proof.pi_b)) |
| 214 | + proof.pi_c = G1.toObject(G1.toAffine(proof.pi_c)) |
| 215 | + |
| 216 | + proof.protocol = "groth16" |
| 217 | + proof.curve = curve.name |
| 218 | + |
| 219 | + await fdZKey.close() |
| 220 | + await fdWtns.close() |
| 221 | + |
| 222 | + proof = stringifyBigInts(proof) |
| 223 | + publicSignals = stringifyBigInts(publicSignals) |
| 224 | + |
| 225 | + return { proof, publicSignals } |
| 226 | +} |
0 commit comments