diff --git a/src/fflonk_export_calldata.js b/src/fflonk_export_calldata.js index 9999f8d1..14232f69 100644 --- a/src/fflonk_export_calldata.js +++ b/src/fflonk_export_calldata.js @@ -29,7 +29,7 @@ function i2hex(i) { function p256(n) { let nstr = n.toString(16); while (nstr.length < 64) nstr = "0" + nstr; - nstr = `"0x${nstr}"`; + nstr = `0x${nstr}`; return nstr; } @@ -47,30 +47,15 @@ export default async function fflonkExportCallData(_pub, _proof, logger) { inputs = inputs + p256(pub[i]); } - const proofBuff = new Uint8Array(G1.F.n8 * 2 * 4 + Fr.n8 * 16); - - G1.toRprUncompressed(proofBuff, 0, G1.e(proof.polynomials.C1)); - G1.toRprUncompressed(proofBuff, G1.F.n8 * 2, G1.e(proof.polynomials.C2)); - G1.toRprUncompressed(proofBuff, G1.F.n8 * 4, G1.e(proof.polynomials.W1)); - G1.toRprUncompressed(proofBuff, G1.F.n8 * 6, G1.e(proof.polynomials.W2)); - Fr.toRprBE(proofBuff, G1.F.n8 * 8, Fr.e(proof.evaluations.ql)); - Fr.toRprBE(proofBuff, G1.F.n8 * 8 + Fr.n8, Fr.e(proof.evaluations.qr)); - Fr.toRprBE(proofBuff, G1.F.n8 * 8 + Fr.n8 * 2, Fr.e(proof.evaluations.qm)); - Fr.toRprBE(proofBuff, G1.F.n8 * 8 + Fr.n8 * 3, Fr.e(proof.evaluations.qo)); - Fr.toRprBE(proofBuff, G1.F.n8 * 8 + Fr.n8 * 4, Fr.e(proof.evaluations.qc)); - Fr.toRprBE(proofBuff, G1.F.n8 * 8 + Fr.n8 * 5, Fr.e(proof.evaluations.s1)); - Fr.toRprBE(proofBuff, G1.F.n8 * 8 + Fr.n8 * 6, Fr.e(proof.evaluations.s2)); - Fr.toRprBE(proofBuff, G1.F.n8 * 8 + Fr.n8 * 7, Fr.e(proof.evaluations.s3)); - Fr.toRprBE(proofBuff, G1.F.n8 * 8 + Fr.n8 * 8, Fr.e(proof.evaluations.a)); - Fr.toRprBE(proofBuff, G1.F.n8 * 8 + Fr.n8 * 9, Fr.e(proof.evaluations.b)); - Fr.toRprBE(proofBuff, G1.F.n8 * 8 + Fr.n8 * 10, Fr.e(proof.evaluations.c)); - Fr.toRprBE(proofBuff, G1.F.n8 * 8 + Fr.n8 * 11, Fr.e(proof.evaluations.z)); - Fr.toRprBE(proofBuff, G1.F.n8 * 8 + Fr.n8 * 12, Fr.e(proof.evaluations.zw)); - Fr.toRprBE(proofBuff, G1.F.n8 * 8 + Fr.n8 * 13, Fr.e(proof.evaluations.t1w)); - Fr.toRprBE(proofBuff, G1.F.n8 * 8 + Fr.n8 * 14, Fr.e(proof.evaluations.t2w)); - Fr.toRprBE(proofBuff, G1.F.n8 * 8 + Fr.n8 * 15, Fr.e(proof.evaluations.inv)); - - const proofHex = Array.from(proofBuff).map(i2hex).join(""); - - return `0x${proofHex},[${inputs}]`; + return `[${p256(proof.polynomials.C1[0])}, ${p256(proof.polynomials.C1[1])},` + + `${p256(proof.polynomials.C2[0])},${p256(proof.polynomials.C2[1])},` + + `${p256(proof.polynomials.W1[0])},${p256(proof.polynomials.W1[1])},` + + `${p256(proof.polynomials.W2[0])},${p256(proof.polynomials.W2[1])},` + + `${p256(proof.evaluations.ql)},${p256(proof.evaluations.qr)},${p256(proof.evaluations.qm)},` + + `${p256(proof.evaluations.qo)},${p256(proof.evaluations.qc)},${p256(proof.evaluations.s1)},` + + `${p256(proof.evaluations.s2)},${p256(proof.evaluations.s3)},${p256(proof.evaluations.a)},` + + `${p256(proof.evaluations.b)},${p256(proof.evaluations.c)},${p256(proof.evaluations.z)},` + + `${p256(proof.evaluations.zw)},${p256(proof.evaluations.t1w)},${p256(proof.evaluations.t2w)},` + + `${p256(proof.evaluations.inv)}],` + + `[${inputs}]`; } diff --git a/templates/verifier_fflonk.sol.ejs b/templates/verifier_fflonk.sol.ejs index 3183f694..ca70625d 100644 --- a/templates/verifier_fflonk.sol.ejs +++ b/templates/verifier_fflonk.sol.ejs @@ -70,32 +70,32 @@ contract FflonkVerifier { uint256 constant G2y1 = 8495653923123431417604973247489272438418190587263600148770280649306958101930; uint256 constant G2y2 = 4082367875863433681332203403145435568316851327593401208105741076214120093531; - // Proof data - // Byte offset of every parameter in `bytes memory proof` + // Proof calldata + // Byte offset of every parameter of the calldata // Polynomial commitments - uint16 constant pC1 = 32; // [C1]_1 - uint16 constant pC2 = 96; // [C2]_1 - uint16 constant pW1 = 160; // [W]_1 - uint16 constant pW2 = 224; // [W']_1 + uint16 constant pC1 = 4 + 0; // [C1]_1 + uint16 constant pC2 = 4 + 32*2; // [C2]_1 + uint16 constant pW1 = 4 + 32*4; // [W]_1 + uint16 constant pW2 = 4 + 32*6; // [W']_1 // Opening evaluations - uint16 constant pEval_ql = 288; // q_L(xi) - uint16 constant pEval_qr = 320; // q_R(xi) - uint16 constant pEval_qm = 352; // q_M(xi) - uint16 constant pEval_qo = 384; // q_O(xi) - uint16 constant pEval_qc = 416; // q_C(xi) - uint16 constant pEval_s1 = 448; // S_{sigma_1}(xi) - uint16 constant pEval_s2 = 480; // S_{sigma_2}(xi) - uint16 constant pEval_s3 = 512; // S_{sigma_3}(xi) - uint16 constant pEval_a = 544; // a(xi) - uint16 constant pEval_b = 576; // b(xi) - uint16 constant pEval_c = 608; // c(xi) - uint16 constant pEval_z = 640; // z(xi) - uint16 constant pEval_zw = 672; // z_omega(xi) - uint16 constant pEval_t1w = 704; // T_1(xi omega) - uint16 constant pEval_t2w = 736; // T_2(xi omega) - uint16 constant pEval_inv = 768; // inv(batch) sent by the prover to avoid any inverse calculation to save gas, - // we check the correctness of the inv(batch) by computing batch - // and checking inv(batch) * batch == 1 + uint16 constant pEval_ql = 4 + 32*8; // q_L(xi) + uint16 constant pEval_qr = 4 + 32*9; // q_R(xi) + uint16 constant pEval_qm = 4 + 32*10; // q_M(xi) + uint16 constant pEval_qo = 4 + 32*11; // q_O(xi) + uint16 constant pEval_qc = 4 + 32*12; // q_C(xi) + uint16 constant pEval_s1 = 4 + 32*13; // S_{sigma_1}(xi) + uint16 constant pEval_s2 = 4 + 32*14; // S_{sigma_2}(xi) + uint16 constant pEval_s3 = 4 + 32*15; // S_{sigma_3}(xi) + uint16 constant pEval_a = 4 + 32*16; // a(xi) + uint16 constant pEval_b = 4 + 32*17; // b(xi) + uint16 constant pEval_c = 4 + 32*18; // c(xi) + uint16 constant pEval_z = 4 + 32*19; // z(xi) + uint16 constant pEval_zw = 4 + 32*20; // z_omega(xi) + uint16 constant pEval_t1w = 4 + 32*21; // T_1(xi omega) + uint16 constant pEval_t2w = 4 + 32*22; // T_2(xi omega) + uint16 constant pEval_inv = 4 + 32*23; // inv(batch) sent by the prover to avoid any inverse calculation to save gas, + // we check the correctness of the inv(batch) by computing batch + // and checking inv(batch) * batch == 1 // Memory data // Challenges @@ -152,17 +152,18 @@ contract FflonkVerifier { uint16 constant pLiS1Inv = <%= 224 + 32 * 34 + 64 * 4 %>; // Reserve 4 * 32 bytes to compute r_1(X) uint16 constant pLiS2Inv = <%= 224 + 32 * 38 + 64 * 4 %>; // Reserve 6 * 32 bytes to compute r_2(X) // Lagrange evaluations - <% for (let i = 1; i <= Math.max(nPublic, 1); i++) { - %>uint16 constant pEval_l<%=i%> = <%= 224 + 32 * (43 + i) + 64 * 4 %>; - <% } %> <% let pLastMem = 224 + 32 * (44 + Math.max(nPublic,1)) + 64 * 4 %> - uint16 constant lastMem = <%=pLastMem%>; - - function verifyProof(bytes memory proof, uint256[<%= nPublic %>] calldata pubSignals) public view returns (bool) { + <% for (let i = 1; i <= Math.max(nPublic, 1); i++) { %> + uint16 constant pEval_l<%=i%> = <%= 224 + 32 * (43 + i) + 64 * 4 %>; + <% } %> + <% let pLastMem = 224 + 32 * (44 + Math.max(nPublic,1)) + 64 * 4 %> + uint16 constant lastMem = <%= pLastMem %>; + + function verifyProof(bytes32[24] calldata proof, uint256[1] calldata pubSignals) public view returns (bool) { assembly { // Computes the inverse of an array of values // See https://vitalik.ca/general/2018/07/21/starks_part_3.html in section where explain fields operations // To save the inverse to be computed on chain the prover sends the inverse as an evaluation in proof.eval_inv - function inverseArray(pProof, pVals, n) { + function inverseArray(pVals, n) { let pAux := mload(0x40) // Point to the next free position let pIn := pVals let lastPIn := add(pVals, mul(n, 32)) // Read n elemnts @@ -178,7 +179,7 @@ contract FflonkVerifier { acc := mulmod(acc, mload(pIn), q) } - let inv := mload(add(pProof, pEval_inv)) + let inv := calldataload(pEval_inv) // Before using the inverse sent by the prover the verifier checks inv(batch) * batch === 1 if iszero(eq(1, mulmod(acc, inv, q))) { @@ -214,8 +215,8 @@ contract FflonkVerifier { } function checkPointBelongsToBN128Curve(p) { - let x := mload(p) - let y := mload(add(p, 32)) + let x := calldataload(p) + let y := calldataload(add(p, 32)) // Check that the point is on the curve // y^2 = x^3 + 3 @@ -229,58 +230,54 @@ contract FflonkVerifier { } // Validate all the evaluations sent by the prover ∈ F - function checkInput(pProof) { - if iszero(eq(mload(pProof), 768)) { - mstore(0, 0) - return(0, 0x20) - } - + function checkInput() { // Check proof commitments fullfill bn128 curve equation Y^2 = X^3 + 3 - checkPointBelongsToBN128Curve(add(pProof, pC1)) - checkPointBelongsToBN128Curve(add(pProof, pC2)) - checkPointBelongsToBN128Curve(add(pProof, pW1)) - checkPointBelongsToBN128Curve(add(pProof, pW2)) - - checkField(mload(add(pProof, pEval_ql))) - checkField(mload(add(pProof, pEval_qr))) - checkField(mload(add(pProof, pEval_qm))) - checkField(mload(add(pProof, pEval_qo))) - checkField(mload(add(pProof, pEval_qc))) - checkField(mload(add(pProof, pEval_s1))) - checkField(mload(add(pProof, pEval_s2))) - checkField(mload(add(pProof, pEval_s3))) - checkField(mload(add(pProof, pEval_a))) - checkField(mload(add(pProof, pEval_b))) - checkField(mload(add(pProof, pEval_c))) - checkField(mload(add(pProof, pEval_z))) - checkField(mload(add(pProof, pEval_zw))) - checkField(mload(add(pProof, pEval_t1w))) - checkField(mload(add(pProof, pEval_t2w))) - checkField(mload(add(pProof, pEval_inv))) + checkPointBelongsToBN128Curve(pC1) + checkPointBelongsToBN128Curve(pC2) + checkPointBelongsToBN128Curve(pW1) + checkPointBelongsToBN128Curve(pW2) + + checkField(calldataload(pEval_ql)) + checkField(calldataload(pEval_qr)) + checkField(calldataload(pEval_qm)) + checkField(calldataload(pEval_qo)) + checkField(calldataload(pEval_qc)) + checkField(calldataload(pEval_s1)) + checkField(calldataload(pEval_s2)) + checkField(calldataload(pEval_s3)) + checkField(calldataload(pEval_a)) + checkField(calldataload(pEval_b)) + checkField(calldataload(pEval_c)) + checkField(calldataload(pEval_z)) + checkField(calldataload(pEval_zw)) + checkField(calldataload(pEval_t1w)) + checkField(calldataload(pEval_t2w)) + checkField(calldataload(pEval_inv)) // Points are checked in the point operations precompiled smart contracts } - function computeChallenges(pProof, pMem, pPublic) { + function computeChallenges(pMem, pPublic) { // Compute challenge.beta & challenge.gamma mstore(add(pMem, <%= pLastMem %> ), C0x) mstore(add(pMem, <%= pLastMem + 32 %> ), C0y) mstore(add(pMem, <%= pLastMem + 64 %>), calldataload(pPublic)) - <%for (let i=1; imstore(add(pMem, <%= pLastMem + 64 + i * 32 %> ), calldataload(add(pPublic, <%= i * 32 %>))) + <%for (let i=1; i + mstore(add(pMem, <%= pLastMem + 64 + i * 32 %> ), calldataload(add(pPublic, <%= i * 32 %>))) <%}%> + - mstore(add(pMem, <%= pLastMem + nPublic * 32 + 64 %> ), mload(add(pProof, pC1))) - mstore(add(pMem, <%= pLastMem + nPublic * 32 + 96 %> ), mload(add(pProof, add(pC1, 32)))) + mstore(add(pMem, <%= pLastMem + nPublic * 32 + 64 %> ), calldataload(pC1)) + mstore(add(pMem, <%= pLastMem + nPublic * 32 + 96 %> ), calldataload(add(pC1, 32))) mstore(add(pMem, pBeta), mod(keccak256(add(pMem, lastMem), <%= nPublic * 32 + 128 %>), q)) mstore(add(pMem, pGamma), mod(keccak256(add(pMem, pBeta), 32), q)) // Get xiSeed & xiSeed2 mstore(add(pMem, lastMem), mload(add(pMem, pGamma))) - mstore(add(pMem, <%= pLastMem + 32 %>), mload(add(pProof, pC2))) - mstore(add(pMem, <%= pLastMem + 64 %>), mload(add(pProof, add(pC2, 32)))) + mstore(add(pMem, <%= pLastMem + 32 %>), calldataload(pC2)) + mstore(add(pMem, <%= pLastMem + 64 %>), calldataload(add(pC2, 32))) let xiSeed := mod(keccak256(add(pMem, lastMem), 96), q) mstore(add(pMem, pXiSeed), xiSeed) @@ -316,36 +313,24 @@ contract FflonkVerifier { mstore(add(pMem, pXi), xin) // Compute xi^n - <%for ( let i = 0; i < power; i++) { - %>xin:= mulmod(xin, xin, q) + <%for ( let i = 0; i < power; i++) { %> + xin:= mulmod(xin, xin, q) <%}%> + xin:= mod(add(sub(xin, 1), q), q) mstore(add(pMem, pZh), xin) mstore(add(pMem, pZhInv), xin) // We will invert later together with lagrange pols // Compute challenge.alpha mstore(add(pMem, lastMem), xiSeed) - mstore(add(pMem, <%= pLastMem + 32 %>), mload(add(pProof, pEval_ql))) - mstore(add(pMem, <%= pLastMem + 2 * 32 %>), mload(add(pProof, pEval_qr))) - mstore(add(pMem, <%= pLastMem + 3 * 32 %>), mload(add(pProof, pEval_qm))) - mstore(add(pMem, <%= pLastMem + 4 * 32 %>), mload(add(pProof, pEval_qo))) - mstore(add(pMem, <%= pLastMem + 5 * 32 %>), mload(add(pProof, pEval_qc))) - mstore(add(pMem, <%= pLastMem + 6 * 32 %>), mload(add(pProof, pEval_s1))) - mstore(add(pMem, <%= pLastMem + 7 * 32 %>), mload(add(pProof, pEval_s2))) - mstore(add(pMem, <%= pLastMem + 8 * 32 %>), mload(add(pProof, pEval_s3))) - mstore(add(pMem, <%= pLastMem + 9 * 32 %>), mload(add(pProof, pEval_a))) - mstore(add(pMem, <%= pLastMem + 10 * 32 %>), mload(add(pProof, pEval_b))) - mstore(add(pMem, <%= pLastMem + 11 * 32 %>), mload(add(pProof, pEval_c))) - mstore(add(pMem, <%= pLastMem + 12 * 32 %>), mload(add(pProof, pEval_z))) - mstore(add(pMem, <%= pLastMem + 13 * 32 %>), mload(add(pProof, pEval_zw))) - mstore(add(pMem, <%= pLastMem + 14 * 32 %>), mload(add(pProof, pEval_t1w))) - mstore(add(pMem, <%= pLastMem + 15 * 32 %>), mload(add(pProof, pEval_t2w))) + + calldatacopy(add(pMem, <%= pLastMem + 32 %>), pEval_ql, 480) mstore(add(pMem, pAlpha), mod(keccak256(add(pMem, lastMem), 512), q)) // Compute challenge.y mstore(add(pMem, lastMem), mload(add(pMem, pAlpha))) - mstore(add(pMem, <%= pLastMem + 32 %> ), mload(add(pProof, pW1))) - mstore(add(pMem, <%= pLastMem + 64 %> ), mload(add(pProof, add(pW1, 32)))) + mstore(add(pMem, <%= pLastMem + 32 %> ), calldataload(pW1)) + mstore(add(pMem, <%= pLastMem + 64 %> ), calldataload(add(pW1, 32))) mstore(add(pMem, pY), mod(keccak256(add(pMem, lastMem), 96), q)) } @@ -363,7 +348,7 @@ contract FflonkVerifier { } // Prepare all the denominators that must be inverted, placed them in consecutive memory addresses - function computeInversions(pProof, pMem) { + function computeInversions(pMem) { // 1/ZH(xi) used in steps 8 and 9 of the verifier to multiply by 1/Z_H(xi) // Value computed during computeChallenges function and stores in pMem+pZhInv @@ -386,43 +371,46 @@ contract FflonkVerifier { mstore(add(pMem, pDenH2), w) // Denominator needed in the verifier when computing L_i^{S1}(X) - <%for ( let i = 0; i < 8; i++) { - %>mstore(add(pMem, add(pLiS0Inv, <%= i * 32 %>)), calcLagrangeItem(pMem, <%= i %>, 7, add(pH0w8_0, <%= i * 32 %>), pH0w8_0)) + <%for ( let i = 0; i < 8; i++) { %> + mstore(add(pMem, add(pLiS0Inv, <%= i * 32 %>)), calcLagrangeItem(pMem, <%= i %>, 7, add(pH0w8_0, <%= i * 32 %>), pH0w8_0)) <%}%> + // Denominator needed in the verifier when computing L_i^{S1}(X) - <%for ( let i = 0; i < 4; i++) { - %>mstore(add(pMem, add(pLiS1Inv, <%= i * 32 %>)), calcLagrangeItem(pMem, <%= i %>, 3, add(pH1w4_0, <%= i * 32 %>), pH1w4_0)) + <%for ( let i = 0; i < 4; i++) {%> + mstore(add(pMem, add(pLiS1Inv, <%= i * 32 %>)), calcLagrangeItem(pMem, <%= i %>, 3, add(pH1w4_0, <%= i * 32 %>), pH1w4_0)) <%}%> + // Denominator needed in the verifier when computing L_i^{S2}(X) - <%for ( let i = 0; i < 6; i++) { - %>mstore(add(pMem, add(pLiS2Inv, <%= i * 32 %>)), calcLagrangeItem(pMem, <%= i %>, 5, add(pH2w3_0, <%= i * 32 %>), pH2w3_0)) + <%for ( let i = 0; i < 6; i++) { %> + mstore(add(pMem, add(pLiS2Inv, <%= i * 32 %>)), calcLagrangeItem(pMem, <%= i %>, 5, add(pH2w3_0, <%= i * 32 %>), pH2w3_0)) <%}%> + // L_i where i from 1 to num public inputs, needed in step 6 and 7 of the verifier to compute L_1(xi) and PI(xi) w := 1 let xi := mload(add(pMem, pXi)) - <% for (let i=1; i<=Math.max(nPublic, 1); i++) { - %>mstore(add(pMem, pEval_l<%=i%>), mulmod(n, mod(add(sub(xi, w), q), q), q)) - <% if (iw := mulmod(w, w1, q) + <% for (let i=1; i<=Math.max(nPublic, 1); i++) { %> + mstore(add(pMem, pEval_l<%=i%>), mulmod(n, mod(add(sub(xi, w), q), q), q)) + <% if (i + w := mulmod(w, w1, q) <% } } %> + // Execute Montgomery batched inversions of the previous prepared values - inverseArray(pProof, add(pMem, pZhInv), <%= Math.max(nPublic, 1) + 21 %>) - } + inverseArray(add(pMem, pZhInv), <%= Math.max(nPublic, 1) + 21 %>) } // Compute Lagrange polynomial evaluation L_i(xi) function computeLagrange(pMem) { let zh := mload(add(pMem, pZh)) let w := 1 <% for (let i=1; i<=Math.max(nPublic, 1); i++) { - if (i===1) { - %>mstore(add(pMem, pEval_l1 ), mulmod(mload(add(pMem, pEval_l1 )), zh, q)) - <% } else { - %>mstore(add(pMem, pEval_l<%=i%>), mulmod(w, mulmod(mload(add(pMem, pEval_l<%=i%>)), zh, q), q)) - <% } - if (iw := mulmod(w, w1, q) - <% } + if (i===1) { %> + mstore(add(pMem, pEval_l1 ), mulmod(mload(add(pMem, pEval_l1 )), zh, q)) + <% } else { %> + mstore(add(pMem, pEval_l<%=i%>), mulmod(w, mulmod(mload(add(pMem, pEval_l<%=i%>)), zh, q), q)) + <% } + if (i + w := mulmod(w, w1, q) + <% } } %> } @@ -430,8 +418,8 @@ contract FflonkVerifier { function computePi(pMem, pPub) { let pi := 0 pi := mod(add(sub(pi, mulmod(mload(add(pMem, pEval_l1)), calldataload(pPub), q)), q), q) - <% for (let i=1; ipi := mod(add(sub(pi, mulmod(mload(add(pMem, pEval_l<%= i + 1 %>)), calldataload(add(pPub, <%= 32 * i %>)), q)), q), q) + <% for (let i=1; i + pi := mod(add(sub(pi, mulmod(mload(add(pMem, pEval_l<%= i + 1 %>)), calldataload(add(pPub, <%= 32 * i %>)), q)), q), q) <% } %> mstore(add(pMem, pPi), pi) } @@ -440,7 +428,7 @@ contract FflonkVerifier { // where x = {h9, h0w8, h0w8^2, h0w8^3, h0w8^4, h0w8^5, h0w8^6, h0w8^7} // and y = {C0(h0), C0(h0w8), C0(h0w8^2), C0(h0w8^3), C0(h0w8^4), C0(h0w8^5), C0(h0w8^6), C0(h0w8^7)} // and computing C0(xi) - function computeR0(pProof, pMem) { + function computeR0(pMem) { let res for { let i := 0 } lt(i, 8) { i := add(i, 1) } { @@ -448,19 +436,19 @@ contract FflonkVerifier { // + (h0w8i)^5 S1 + (h0w8i)^6 S2 + (h0w8i)^7 S3 let h0w80 := mload(add(pMem, add(pH0w8_0, mul(i, 32)))) - let c0Value := addmod(mload(add(pProof, pEval_ql)), mulmod(mload(add(pProof, pEval_qr)), h0w80, q), q) + let c0Value := addmod(calldataload(pEval_ql), mulmod(calldataload(pEval_qr), h0w80, q), q) let h0w8i := mulmod(h0w80, h0w80, q) - c0Value := addmod(c0Value, mulmod(mload(add(pProof, pEval_qo)), h0w8i, q), q) + c0Value := addmod(c0Value, mulmod(calldataload(pEval_qo), h0w8i, q), q) h0w8i := mulmod(h0w8i, h0w80, q) - c0Value := addmod(c0Value, mulmod(mload(add(pProof, pEval_qm)), h0w8i, q), q) + c0Value := addmod(c0Value, mulmod(calldataload(pEval_qm), h0w8i, q), q) h0w8i := mulmod(h0w8i, h0w80, q) - c0Value := addmod(c0Value, mulmod(mload(add(pProof, pEval_qc)), h0w8i, q), q) + c0Value := addmod(c0Value, mulmod(calldataload(pEval_qc), h0w8i, q), q) h0w8i := mulmod(h0w8i, h0w80, q) - c0Value := addmod(c0Value, mulmod(mload(add(pProof, pEval_s1)), h0w8i, q), q) + c0Value := addmod(c0Value, mulmod(calldataload(pEval_s1), h0w8i, q), q) h0w8i := mulmod(h0w8i, h0w80, q) - c0Value := addmod(c0Value, mulmod(mload(add(pProof, pEval_s2)), h0w8i, q), q) + c0Value := addmod(c0Value, mulmod(calldataload(pEval_s2), h0w8i, q), q) h0w8i := mulmod(h0w8i, h0w80, q) - c0Value := addmod(c0Value, mulmod(mload(add(pProof, pEval_s3)), h0w8i, q), q) + c0Value := addmod(c0Value, mulmod(calldataload(pEval_s3), h0w8i, q), q) // Compute Lagrange evaluation let lagrange := calcLagrangeItem(pMem, i, 7, pY, pH0w8_0) @@ -476,17 +464,17 @@ contract FflonkVerifier { // where x = {h1, h1w4, h1w4^2, h1w4^3} // and y = {C1(h1), C1(h1w4), C1(h1w4^2), C1(h1w4^3)} // and computing T0(xi) - function computeR1(pProof, pMem) { + function computeR1(pMem) { let t0 - let evalA := mload(add(pProof, pEval_a)) - let evalB := mload(add(pProof, pEval_b)) - let evalC := mload(add(pProof, pEval_c)) - - t0 := mulmod(mload(add(pProof, pEval_ql)), evalA, q) - t0 := addmod(t0, mulmod(mload(add(pProof, pEval_qr)), evalB, q) ,q) - t0 := addmod(t0, mulmod(mload(add(pProof, pEval_qm)), mulmod(evalA, evalB, q), q) ,q) - t0 := addmod(t0, mulmod(mload(add(pProof, pEval_qo)), evalC, q) ,q) - t0 := addmod(t0, mload(add(pProof, pEval_qc)) ,q) + let evalA := calldataload(pEval_a) + let evalB := calldataload(pEval_b) + let evalC := calldataload(pEval_c) + + t0 := mulmod(calldataload(pEval_ql), evalA, q) + t0 := addmod(t0, mulmod(calldataload(pEval_qr), evalB, q) ,q) + t0 := addmod(t0, mulmod(calldataload(pEval_qm), mulmod(evalA, evalB, q), q) ,q) + t0 := addmod(t0, mulmod(calldataload(pEval_qo), evalC, q) ,q) + t0 := addmod(t0, calldataload(pEval_qc) ,q) t0 := addmod(t0, mload(add(pMem, pPi)), q) t0 := mulmod(t0, mload(add(pMem, pZhInv)), q) @@ -514,29 +502,29 @@ contract FflonkVerifier { // where x = {[h2, h2w3, h2w3^2], [h3, h3w3, h3w3^2]} // and y = {[C2(h2), C2(h2w3), C2(h2w3^2)], [C2(h3), C2(h3w3), C2(h3w3^2)]} // and computing T1(xi) and T2(xi) - function computeR2(pProof, pMem) { + function computeR2(pMem) { let t1 let t2 let betaXi := mulmod(mload(add(pMem, pBeta)), mload(add(pMem, pXi)), q) let gamma := mload(add(pMem, pGamma)) - let evalZ := mload(add(pProof, pEval_z)) - let evalZw := mload(add(pProof, pEval_zw)) + let evalZ := calldataload( pEval_z) + let evalZw := calldataload( pEval_zw) - t2 := addmod(mload(add(pProof, pEval_a)), addmod(betaXi, gamma, q) ,q) + t2 := addmod(calldataload( pEval_a), addmod(betaXi, gamma, q) ,q) t2 := mulmod(t2, - addmod(mload(add(pProof, pEval_b)), + addmod(calldataload( pEval_b), addmod(mulmod(betaXi, k1, q), gamma, q) ,q), q) t2 := mulmod(t2, - addmod(mload(add(pProof, pEval_c)), + addmod(calldataload( pEval_c), addmod(mulmod(betaXi, k2, q), gamma, q) ,q), q) t2 := mulmod(t2, evalZ, q) //Let's use t1 as a temporal variable to save one local - t1 := addmod(mload(add(pProof, pEval_a)), addmod(mulmod(mload(add(pMem, pBeta)), mload(add(pProof, pEval_s1)), q), gamma, q) ,q) + t1 := addmod(calldataload(pEval_a), addmod(mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q), gamma, q) ,q) t1 := mulmod(t1, - addmod(mload(add(pProof, pEval_b)), addmod(mulmod(mload(add(pMem, pBeta)), mload(add(pProof, pEval_s2)), q), gamma, q) ,q), q) + addmod(calldataload(pEval_b), addmod(mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q), gamma, q) ,q), q) t1 := mulmod(t1, - addmod(mload(add(pProof, pEval_c)), addmod(mulmod(mload(add(pMem, pBeta)), mload(add(pProof, pEval_s3)), q), gamma, q) ,q), q) + addmod(calldataload(pEval_c), addmod(mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s3), q), gamma, q) ,q), q) t1 := mulmod(t1, evalZw, q) t2:= addmod(t2, mod(sub(q, t1), q), q) @@ -559,8 +547,8 @@ contract FflonkVerifier { c2Value := addmod(c2Value, mulmod(mulmod(hw, hw, q), t2, q), q) } if gt(i, 2) { - c2Value := addmod(evalZw, mulmod(hw, mload(add(pProof, pEval_t1w)), q), q) - c2Value := addmod(c2Value, mulmod(mulmod(hw, hw, q), mload(add(pProof, pEval_t2w)), q), q) + c2Value := addmod(evalZw, mulmod(hw, calldataload(pEval_t1w), q), q) + c2Value := addmod(c2Value, mulmod(mulmod(hw, hw, q), calldataload(pEval_t2w), q), q) } let lagrange := calcLagrangeItem(pMem, i, 5, pY, pH2w3_0) @@ -592,8 +580,8 @@ contract FflonkVerifier { function g1_mulAcc(pR, pP, s) { let success let mIn := mload(0x40) - mstore(mIn, mload(pP)) - mstore(add(mIn, 32), mload(add(pP, 32))) + mstore(mIn, calldataload(pP)) + mstore(add(mIn, 32), calldataload(add(pP, 32))) mstore(add(mIn, 64), s) success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64) @@ -640,7 +628,7 @@ contract FflonkVerifier { } } - function computeFEJ(pProof, pMem) { + function computeFEJ(pMem) { // Prepare shared numerator between F, E and J to reuse it let y := mload(add(pMem, pY)) let numerator := addmod(y, mod(sub(q, mload(add(pMem, pH0w8_0))), q), q) @@ -659,18 +647,18 @@ contract FflonkVerifier { // Compute full batched polynomial commitment [F]_1 mstore(add(pMem, pF), C0x) mstore(add(pMem, add(pF, 32)), C0y) - g1_mulAcc(add(pMem, pF), add(pProof, pC1), quotient1) - g1_mulAcc(add(pMem, pF), add(pProof, pC2), quotient2) + g1_mulAcc(add(pMem, pF), pC1, quotient1) + g1_mulAcc(add(pMem, pF), pC2, quotient2) // Compute group-encoded batch evaluation [E]_1 g1_mulAccC(add(pMem, pE), G1x, G1y, addmod(mload(add(pMem, pR0)), addmod(mulmod(quotient1, mload(add(pMem, pR1)),q), mulmod(quotient2, mload(add(pMem, pR2)),q), q), q)) // Compute the full difference [J]_1 - g1_mulAcc(add(pMem, pJ), add(pProof, pW1), numerator) + g1_mulAcc(add(pMem, pJ), pW1, numerator) } // Validate all evaluations with a pairing checking that e([F]_1 - [E]_1 - [J]_1 + y[W2]_1, [1]_2) == e([W']_1, [x]_2) - function checkPairing(pProof, pMem) -> isOk { + function checkPairing(pMem) -> isOk { let mIn := mload(0x40) // First pairing value @@ -681,7 +669,7 @@ contract FflonkVerifier { // F = F - E - J + y·W2 g1_acc(add(pMem, pF), add(pMem, pE)) g1_acc(add(pMem, pF), add(pMem, pJ)) - g1_mulAcc(add(pMem, pF), add(pProof, pW2), mload(add(pMem, pY))) + g1_mulAcc(add(pMem, pF), pW2, mload(add(pMem, pY))) mstore(mIn, mload(add(pMem, pF))) mstore(add(mIn, 32), mload(add(add(pMem, pF), 32))) @@ -694,8 +682,8 @@ contract FflonkVerifier { // Third pairing value // Compute -W2 - mstore(add(mIn, 192), mload(add(pProof, pW2))) - let s := mload(add(add(pProof, pW2), 32)) + mstore(add(mIn, 192), calldataload(pW2)) + let s := calldataload(add(pW2, 32)) s := mod(sub(qf, s), qf) mstore(add(mIn, 224), s) @@ -714,10 +702,10 @@ contract FflonkVerifier { mstore(0x40, add(pMem, lastMem)) // Validate that all evaluations ∈ F - checkInput(proof) + checkInput() // Compute the challenges: beta, gamma, xi, alpha and y ∈ F, h1w4/h2w3/h3w3 roots, xiN and zh(xi) - computeChallenges(proof, pMem, pubSignals) + computeChallenges(pMem, pubSignals) // To divide prime fields the Extended Euclidean Algorithm for computing modular inverses is needed. // The Montgomery batch inversion algorithm allow us to compute n inverses reducing to a single one inversion. @@ -727,7 +715,7 @@ contract FflonkVerifier { // 1) Prepare all the denominators to inverse // 2) Check the inverse sent by the prover it is what it should be // 3) Compute the others inverses using the Montgomery Batched Algorithm using the inverse sent to avoid the inversion operation it does. - computeInversions(proof, pMem) + computeInversions(pMem) // Compute Lagrange polynomial evaluations Li(xi) computeLagrange(pMem) @@ -736,15 +724,15 @@ contract FflonkVerifier { computePi(pMem, pubSignals) // Computes r1(y) and r2(y) - computeR0(proof, pMem) - computeR1(proof, pMem) - computeR2(proof, pMem) + computeR0(pMem) + computeR1(pMem) + computeR2(pMem) // Compute full batched polynomial commitment [F]_1, group-encoded batch evaluation [E]_1 and the full difference [J]_1 - computeFEJ(proof, pMem) + computeFEJ(pMem) // Validate all evaluations - let isValid := checkPairing(proof, pMem) + let isValid := checkPairing(pMem) mstore(0, isValid) return(0, 0x20) diff --git a/test/smart_contracts.test.js b/test/smart_contracts.test.js index f856634a..440e2faa 100644 --- a/test/smart_contracts.test.js +++ b/test/smart_contracts.test.js @@ -143,9 +143,8 @@ describe("Smart contracts test suite", function () { // Verifiy the proof in the smart contract const { evaluations, polynomials } = proofJson; - const arrayStrings = Array(24).fill("bytes32"); - const proof = ethers.utils.defaultAbiCoder.encode( - arrayStrings, + + const proof = [ ethers.utils.hexZeroPad(ethers.BigNumber.from(polynomials.C1[0]).toHexString(), 32), ethers.utils.hexZeroPad(ethers.BigNumber.from(polynomials.C1[1]).toHexString(), 32), @@ -171,8 +170,7 @@ describe("Smart contracts test suite", function () { ethers.utils.hexZeroPad(ethers.BigNumber.from(evaluations.t1w).toHexString(), 32), ethers.utils.hexZeroPad(ethers.BigNumber.from(evaluations.t2w).toHexString(), 32), ethers.utils.hexZeroPad(ethers.BigNumber.from(evaluations.inv).toHexString(), 32), - ], - ); + ]; expect(await verifierContract.verifyProof(proof, publicInputs)).to.be.equal(true); });