mirror of
https://github.com/arnaucube/sonobe.git
synced 2026-01-10 16:01:35 +01:00
Add solidity groth16, kzg10 and final decider verifiers in a dedicated workspace (#70)
* change: Refactor structure into workspace * chore: Add empty readme * change: Transform repo into workspace * add: Create folding-verifier-solidity crate * add: Include askama.toml for `sol` extension escaper * add: Jordi's old Groth16 verifier .sol template and adapt it * tmp: create simple template struct to test * Update FoldingSchemes trait, fit Nova+CycleFold - update lib.rs's `FoldingScheme` trait interface - fit Nova+CycleFold into the `FoldingScheme` trait - refactor `src/nova/*` * chore: add serialization assets for testing Now we include an `assets` folder with a serialized proof & vk for tests * Add `examples` dir, with Nova's `FoldingScheme` example * polishing * expose poseidon_test_config outside tests * change: Refactor structure into workspace * chore: Add empty readme * change: Transform repo into workspace * add: Create folding-verifier-solidity crate * add: Include askama.toml for `sol` extension escaper * add: Jordi's old Groth16 verifier .sol template and adapt it * tmp: create simple template struct to test * feat: templating kzg working * chore: add emv and revm * feat: start evm file * chore: add ark-poly-commit * chore: move `commitment` to `folding-schemes` * chore: update `.gitignore` to ignore generated contracts * chore: update template with bn254 lib on it (avoids import), update for loop to account for whitespaces * refactor: update template with no lib * feat: add evm deploy code, compile and create kzg verifier * chore: update `Cargo.toml` to have `folding-schemes` available with verifiers * feat: start kzg prove and verify with sol * chore: compute crs from kzg prover * feat: evm kzg verification passing * tmp * change: Swap order of G2 coordinates within the template * Update way to serialize proof with correct order * chore: update `Cargo.toml` * chore: add revm * chore: add `save_solidity` * refactor: verifiers in dedicated mod * refactor: have dedicated `utils` module * chore: expose modules * chore: update verifier for kzg * chore: rename templates * fix: look for binary using also name of contract * refactor: generate groth16 proof for sha256 pre-image, generate groth16 template with verifying key * chore: template renaming * fix: switch circuit for circuit that simply adds * feat: generates test data on the fly * feat: update to latest groth16 verifier * refactor: rename folder, update `.gitignore` * chore: update `Cargo.toml` * chore: update templates extension to indicate that they are templates * chore: rename templates, both files and structs * fix: template inheritance working * feat: template spdx and pragma statements * feat: decider verifier compiles, update test for kzg10 and groth16 templates * feat: parameterize which size of the crs should be stored on the contract * chore: add comment on how the groth16 and kzg10 proofs will be linked together * chore: cargo clippy run * chore: cargo clippy tests * chore: cargo fmt * refactor: remove unused lifetime parameter * chore: end merge * chore: move examples to `folding-schemes` workspace * get latest main changes * fix: temp fix clippy warnings, will remove lints once not used in tests only * fix: cargo clippy lint added on `code_size` * fix: update path to test circuit and add step for installing solc * chore: remove `save_solidity` steps * fix: the borrowed expression implements the required traits * chore: update `Cargo.toml` * chore: remove extra `[patch.crates-io]` * fix: update to patch at the workspace level and add comment explaining this * refactor: correct `staticcall` with valid input/output sizes and change return syntax for pairing * refactor: expose modules and remove `dead_code` calls * chore: update `README.md`, add additional comments on `kzg10` template and update `groth16` template comments * chore: be clearer on attributions on `kzg10` --------- Co-authored-by: CPerezz <c.perezbaro@gmail.com> Co-authored-by: arnaucube <root@arnaucube.com>
This commit is contained in:
172
folding-schemes-solidity/templates/groth16_verifier.askama.sol
Normal file
172
folding-schemes-solidity/templates/groth16_verifier.askama.sol
Normal file
@@ -0,0 +1,172 @@
|
||||
{{ sdpx }}
|
||||
/*
|
||||
Copyright 2021 0KIMS association.
|
||||
|
||||
* `folding-schemes-solidity` added comment
|
||||
This file is a template built out of [snarkJS](https://github.com/iden3/snarkjs) groth16 verifier.
|
||||
See the original ejs template [here](https://github.com/iden3/snarkjs/blob/master/templates/verifier_groth16.sol.ejs)
|
||||
*
|
||||
|
||||
snarkJS is a free software: you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
snarkJS is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with snarkJS. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
{{ pragma_version }}
|
||||
|
||||
contract Groth16Verifier {
|
||||
// Scalar field size
|
||||
uint256 constant r = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
// Base field size
|
||||
uint256 constant q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
|
||||
// Verification Key data
|
||||
uint256 constant alphax = {{ vkey_alpha_g1.0[0] }};
|
||||
uint256 constant alphay = {{ vkey_alpha_g1.0[1] }};
|
||||
uint256 constant betax1 = {{ vkey_beta_g2.0[0][1] }};
|
||||
uint256 constant betax2 = {{ vkey_beta_g2.0[0][0] }};
|
||||
uint256 constant betay1 = {{ vkey_beta_g2.0[1][1] }};
|
||||
uint256 constant betay2 = {{ vkey_beta_g2.0[1][0] }};
|
||||
uint256 constant gammax1 = {{ vkey_gamma_g2.0[0][1] }};
|
||||
uint256 constant gammax2 = {{ vkey_gamma_g2.0[0][0] }};
|
||||
uint256 constant gammay1 = {{ vkey_gamma_g2.0[1][1] }};
|
||||
uint256 constant gammay2 = {{ vkey_gamma_g2.0[1][0] }};
|
||||
uint256 constant deltax1 = {{ vkey_delta_g2.0[0][1] }};
|
||||
uint256 constant deltax2 = {{ vkey_delta_g2.0[0][0] }};
|
||||
uint256 constant deltay1 = {{ vkey_delta_g2.0[1][1] }};
|
||||
uint256 constant deltay2 = {{ vkey_delta_g2.0[1][0] }};
|
||||
|
||||
{% for (i, point) in gamma_abc_g1.iter().enumerate() %}
|
||||
uint256 constant IC{{i}}x = {{ point.0[0] }};
|
||||
uint256 constant IC{{i}}y = {{ point.0[1] }};
|
||||
{% endfor %}
|
||||
|
||||
// Memory data
|
||||
uint16 constant pVk = 0;
|
||||
uint16 constant pPairing = 128;
|
||||
|
||||
uint16 constant pLastMem = 896;
|
||||
|
||||
function verifyProof(uint[2] calldata _pA, uint[2][2] calldata _pB, uint[2] calldata _pC, uint[{{ gamma_abc_len - 1 }}] calldata _pubSignals) public view returns (bool) {
|
||||
assembly {
|
||||
function checkField(v) {
|
||||
if iszero(lt(v, q)) {
|
||||
mstore(0, 0)
|
||||
return(0, 0x20)
|
||||
}
|
||||
}
|
||||
|
||||
// G1 function to multiply a G1 value(x,y) to value in an address
|
||||
function g1_mulAccC(pR, x, y, s) {
|
||||
let success
|
||||
let mIn := mload(0x40)
|
||||
mstore(mIn, x)
|
||||
mstore(add(mIn, 32), y)
|
||||
mstore(add(mIn, 64), s)
|
||||
|
||||
success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64)
|
||||
|
||||
if iszero(success) {
|
||||
mstore(0, 0)
|
||||
return(0, 0x20)
|
||||
}
|
||||
|
||||
mstore(add(mIn, 64), mload(pR))
|
||||
mstore(add(mIn, 96), mload(add(pR, 32)))
|
||||
|
||||
success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
|
||||
|
||||
if iszero(success) {
|
||||
mstore(0, 0)
|
||||
return(0, 0x20)
|
||||
}
|
||||
}
|
||||
|
||||
function checkPairing(pA, pB, pC, pubSignals, pMem) -> isOk {
|
||||
let _pPairing := add(pMem, pPairing)
|
||||
let _pVk := add(pMem, pVk)
|
||||
|
||||
mstore(_pVk, IC0x)
|
||||
mstore(add(_pVk, 32), IC0y)
|
||||
|
||||
// Compute the linear combination vk_x
|
||||
{% for (i, _) in gamma_abc_g1.iter().enumerate() %}
|
||||
{% if loop.first -%}
|
||||
{%- else -%}
|
||||
g1_mulAccC(_pVk, IC{{i}}x, IC{{i}}y, calldataload(add(pubSignals, {{(i-1)*32}})))
|
||||
{%- endif -%}
|
||||
{% endfor %}
|
||||
|
||||
// -A
|
||||
mstore(_pPairing, calldataload(pA))
|
||||
mstore(add(_pPairing, 32), mod(sub(q, calldataload(add(pA, 32))), q))
|
||||
|
||||
// B
|
||||
mstore(add(_pPairing, 64), calldataload(pB))
|
||||
mstore(add(_pPairing, 96), calldataload(add(pB, 32)))
|
||||
mstore(add(_pPairing, 128), calldataload(add(pB, 64)))
|
||||
mstore(add(_pPairing, 160), calldataload(add(pB, 96)))
|
||||
|
||||
// alpha1
|
||||
mstore(add(_pPairing, 192), alphax)
|
||||
mstore(add(_pPairing, 224), alphay)
|
||||
|
||||
// beta2
|
||||
mstore(add(_pPairing, 256), betax1)
|
||||
mstore(add(_pPairing, 288), betax2)
|
||||
mstore(add(_pPairing, 320), betay1)
|
||||
mstore(add(_pPairing, 352), betay2)
|
||||
|
||||
// vk_x
|
||||
mstore(add(_pPairing, 384), mload(add(pMem, pVk)))
|
||||
mstore(add(_pPairing, 416), mload(add(pMem, add(pVk, 32))))
|
||||
|
||||
|
||||
// gamma2
|
||||
mstore(add(_pPairing, 448), gammax1)
|
||||
mstore(add(_pPairing, 480), gammax2)
|
||||
mstore(add(_pPairing, 512), gammay1)
|
||||
mstore(add(_pPairing, 544), gammay2)
|
||||
|
||||
// C
|
||||
mstore(add(_pPairing, 576), calldataload(pC))
|
||||
mstore(add(_pPairing, 608), calldataload(add(pC, 32)))
|
||||
|
||||
// delta2
|
||||
mstore(add(_pPairing, 640), deltax1)
|
||||
mstore(add(_pPairing, 672), deltax2)
|
||||
mstore(add(_pPairing, 704), deltay1)
|
||||
mstore(add(_pPairing, 736), deltay2)
|
||||
|
||||
|
||||
let success := staticcall(sub(gas(), 2000), 8, _pPairing, 768, _pPairing, 0x20)
|
||||
|
||||
isOk := and(success, mload(_pPairing))
|
||||
}
|
||||
|
||||
let pMem := mload(0x40)
|
||||
mstore(0x40, add(pMem, pLastMem))
|
||||
|
||||
// Validate that all evaluations ∈ F
|
||||
{% for (i, _) in gamma_abc_g1.iter().enumerate() %}
|
||||
checkField(calldataload(add(_pubSignals, {{i*32}})))
|
||||
{% endfor %}
|
||||
|
||||
// Validate all evaluations
|
||||
let isValid := checkPairing(_pA, _pB, _pC, _pubSignals, pMem)
|
||||
|
||||
mstore(0, isValid)
|
||||
|
||||
return(0, 0x20)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
{{ groth16_verifier }}
|
||||
|
||||
{{ kzg10_verifier }}
|
||||
|
||||
/**
|
||||
* @author PSE & 0xPARC
|
||||
* @title NovaDecider contract, for verifying zk-snarks Nova IVC proofs.
|
||||
* @dev This is an askama template. It will feature a snarkjs groth16 and a kzg10 verifier, from which this contract inherits.
|
||||
*/
|
||||
contract NovaDecider is Groth16Verifier, KZG10Verifier {
|
||||
function verifyProof(uint[2] calldata _pA, uint[2][2] calldata _pB, uint[2] calldata _pC, uint[{{ gamma_abc_len - 1 }}] calldata _pubSignals, uint256[2] calldata c, uint256[2] calldata pi, uint256 x, uint256 y) public view returns (bool) {
|
||||
require(super.verifyProof(_pA, _pB, _pC, _pubSignals) == true, "Groth16 verification failed");
|
||||
// for now, we do not relate the Groth16 and KZG10 proofs
|
||||
// this will done in the future, by computing challenge points from the groth16 proof's data
|
||||
require(super.check(c, pi, x, y) == true, "KZG10 verification failed");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
275
folding-schemes-solidity/templates/kzg10_verifier.askama.sol
Normal file
275
folding-schemes-solidity/templates/kzg10_verifier.askama.sol
Normal file
@@ -0,0 +1,275 @@
|
||||
{{ sdpx }}
|
||||
|
||||
{{ pragma_version }}
|
||||
|
||||
/**
|
||||
* @author Privacy and Scaling Explorations team - pse.dev
|
||||
* @dev Contains utility functions for ops in BN254; in G_1 mostly.
|
||||
* @notice Forked from https://github.com/weijiekoh/libkzg/tree/master.
|
||||
* Among others, a few of the changes we did on this fork were:
|
||||
* - Templating the pragma version
|
||||
* - Removing type wrappers and use uints instead
|
||||
* - Performing changes on arg types
|
||||
* - Update some of the `require` statements
|
||||
* - Use the bn254 scalar field instead of checking for overflow on the babyjub prime
|
||||
* - In batch checking, we compute auxiliary polynomials and their commitments at the same time.
|
||||
*/
|
||||
contract KZG10Verifier {
|
||||
|
||||
// prime of field F_p over which y^2 = x^3 + 3 is defined
|
||||
uint256 public constant BN254_PRIME_FIELD =
|
||||
21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
uint256 public constant BN254_SCALAR_FIELD =
|
||||
21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
||||
|
||||
/**
|
||||
* @notice Performs scalar multiplication in G_1.
|
||||
* @param p G_1 point to multiply
|
||||
* @param s Scalar to multiply by
|
||||
* @return r G_1 point p multiplied by scalar s
|
||||
*/
|
||||
function mulScalar(uint256[2] memory p, uint256 s) internal view returns (uint256[2] memory r) {
|
||||
uint256[3] memory input;
|
||||
input[0] = p[0];
|
||||
input[1] = p[1];
|
||||
input[2] = s;
|
||||
bool success;
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 7, input, 0x60, r, 0x40)
|
||||
switch success
|
||||
case 0 { invalid() }
|
||||
}
|
||||
require(success, "bn254: scalar mul failed");
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Negates a point in G_1.
|
||||
* @param p G_1 point to negate
|
||||
* @return uint256[2] G_1 point -p
|
||||
*/
|
||||
function negate(uint256[2] memory p) internal pure returns (uint256[2] memory) {
|
||||
if (p[0] == 0 && p[1] == 0) {
|
||||
return p;
|
||||
}
|
||||
return [p[0], BN254_PRIME_FIELD - (p[1] % BN254_PRIME_FIELD)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Adds two points in G_1.
|
||||
* @param p1 G_1 point 1
|
||||
* @param p2 G_1 point 2
|
||||
* @return r G_1 point p1 + p2
|
||||
*/
|
||||
function add(uint256[2] memory p1, uint256[2] memory p2) internal view returns (uint256[2] memory r) {
|
||||
bool success;
|
||||
uint256[4] memory input = [p1[0], p1[1], p2[0], p2[1]];
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 6, input, 0x80, r, 0x40)
|
||||
switch success
|
||||
case 0 { invalid() }
|
||||
}
|
||||
|
||||
require(success, "bn254: point add failed");
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Computes the pairing check e(p1, p2) * e(p3, p4) == 1
|
||||
* @dev Note that G_2 points a*i + b are encoded as two elements of F_p, (a, b)
|
||||
* @param a_1 G_1 point 1
|
||||
* @param a_2 G_2 point 1
|
||||
* @param b_1 G_1 point 2
|
||||
* @param b_2 G_2 point 2
|
||||
* @return result true if pairing check is successful
|
||||
*/
|
||||
function pairing(uint256[2] memory a_1, uint256[2][2] memory a_2, uint256[2] memory b_1, uint256[2][2] memory b_2)
|
||||
internal
|
||||
view
|
||||
returns (bool result)
|
||||
{
|
||||
uint256[12] memory input = [
|
||||
a_1[0],
|
||||
a_1[1],
|
||||
a_2[0][1], // imaginary part first
|
||||
a_2[0][0],
|
||||
a_2[1][1], // imaginary part first
|
||||
a_2[1][0],
|
||||
b_1[0],
|
||||
b_1[1],
|
||||
b_2[0][1], // imaginary part first
|
||||
b_2[0][0],
|
||||
b_2[1][1], // imaginary part first
|
||||
b_2[1][0]
|
||||
];
|
||||
|
||||
uint256[1] memory out;
|
||||
bool success;
|
||||
|
||||
assembly {
|
||||
success := staticcall(sub(gas(), 2000), 8, input, 0x180, out, 0x20)
|
||||
switch success
|
||||
case 0 { invalid() }
|
||||
}
|
||||
|
||||
require(success, "bn254: pairing failed");
|
||||
|
||||
return out[0] == 1;
|
||||
}
|
||||
|
||||
uint256[2] G_1 = [
|
||||
{{ g1.0[0] }},
|
||||
{{ g1.0[1] }}
|
||||
];
|
||||
uint256[2][2] G_2 = [
|
||||
[
|
||||
{{ g2.0[0][0] }},
|
||||
{{ g2.0[0][1] }}
|
||||
],
|
||||
[
|
||||
{{ g2.0[1][0] }},
|
||||
{{ g2.0[1][1] }}
|
||||
]
|
||||
];
|
||||
uint256[2][2] VK = [
|
||||
[
|
||||
{{ vk.0[0][0] }},
|
||||
{{ vk.0[0][1] }}
|
||||
],
|
||||
[
|
||||
{{ vk.0[1][0] }},
|
||||
{{ vk.0[1][1] }}
|
||||
]
|
||||
];
|
||||
|
||||
uint256[2][{{ g1_crs_len }}] G1_CRS = [
|
||||
{%- for (i, point) in g1_crs.iter().enumerate() %}
|
||||
[
|
||||
{{ point.0[0] }},
|
||||
{{ point.0[1] }}
|
||||
{% if loop.last -%}
|
||||
]
|
||||
{%- else -%}
|
||||
],
|
||||
{%- endif -%}
|
||||
{% endfor -%}
|
||||
];
|
||||
|
||||
/**
|
||||
* @notice Verifies a single point evaluation proof. Function name follows `ark-poly`.
|
||||
* @dev To avoid ops in G_2, we slightly tweak how the verification is done.
|
||||
* @param c G_1 point commitment to polynomial.
|
||||
* @param pi G_1 point proof.
|
||||
* @param x Value to prove evaluation of polynomial at.
|
||||
* @param y Evaluation poly(x).
|
||||
* @return result Indicates if KZG proof is correct.
|
||||
*/
|
||||
function check(uint256[2] calldata c, uint256[2] calldata pi, uint256 x, uint256 y)
|
||||
public
|
||||
view
|
||||
returns (bool result)
|
||||
{
|
||||
//
|
||||
// we want to:
|
||||
// 1. avoid gas intensive ops in G2
|
||||
// 2. format the pairing check in line with what the evm opcode expects.
|
||||
//
|
||||
// we can do this by tweaking the KZG check to be:
|
||||
//
|
||||
// e(pi, vk - x * g2) = e(c - y * g1, g2) [initial check]
|
||||
// e(pi, vk - x * g2) * e(c - y * g1, g2)^{-1} = 1
|
||||
// e(pi, vk - x * g2) * e(-c + y * g1, g2) = 1 [bilinearity of pairing for all subsequent steps]
|
||||
// e(pi, vk) * e(pi, -x * g2) * e(-c + y * g1, g2) = 1
|
||||
// e(pi, vk) * e(-x * pi, g2) * e(-c + y * g1, g2) = 1
|
||||
// e(pi, vk) * e(x * -pi - c + y * g1, g2) = 1 [done]
|
||||
// |_ rhs_pairing _|
|
||||
//
|
||||
uint256[2] memory rhs_pairing =
|
||||
add(mulScalar(negate(pi), x), add(negate(c), mulScalar(G_1, y)));
|
||||
return pairing(pi, VK, rhs_pairing, G_2);
|
||||
}
|
||||
|
||||
function evalPolyAt(uint256[] memory _coefficients, uint256 _index) public pure returns (uint256) {
|
||||
uint256 m = BN254_SCALAR_FIELD;
|
||||
uint256 result = 0;
|
||||
uint256 powerOfX = 1;
|
||||
|
||||
for (uint256 i = 0; i < _coefficients.length; i++) {
|
||||
uint256 coeff = _coefficients[i];
|
||||
assembly {
|
||||
result := addmod(result, mulmod(powerOfX, coeff, m), m)
|
||||
powerOfX := mulmod(powerOfX, _index, m)
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Ensures that z(x) == 0 and l(x) == y for all x in x_vals and y in y_vals. It returns the commitment to z(x) and l(x).
|
||||
* @param z_coeffs coefficients of the zero polynomial z(x) = (x - x_1)(x - x_2)...(x - x_n).
|
||||
* @param l_coeffs coefficients of the lagrange polynomial l(x).
|
||||
* @param x_vals x values to evaluate the polynomials at.
|
||||
* @param y_vals y values to which l(x) should evaluate to.
|
||||
* @return uint256[2] commitment to z(x).
|
||||
* @return uint256[2] commitment to l(x).
|
||||
*/
|
||||
function checkAndCommitAuxPolys(
|
||||
uint256[] memory z_coeffs,
|
||||
uint256[] memory l_coeffs,
|
||||
uint256[] memory x_vals,
|
||||
uint256[] memory y_vals
|
||||
) public view returns (uint256[2] memory, uint256[2] memory) {
|
||||
// z(x) is of degree len(x_vals), it is a product of linear polynomials (x - x_i)
|
||||
// l(x) is of degree len(x_vals) - 1
|
||||
uint256[2] memory z_commit;
|
||||
uint256[2] memory l_commit;
|
||||
for (uint256 i = 0; i < x_vals.length; i++) {
|
||||
z_commit = add(z_commit, mulScalar(G1_CRS[i], z_coeffs[i])); // update commitment to z(x)
|
||||
l_commit = add(l_commit, mulScalar(G1_CRS[i], l_coeffs[i])); // update commitment to l(x)
|
||||
|
||||
uint256 eval_z = evalPolyAt(z_coeffs, x_vals[i]);
|
||||
uint256 eval_l = evalPolyAt(l_coeffs, x_vals[i]);
|
||||
|
||||
require(eval_z == 0, "checkAndCommitAuxPolys: wrong zero poly");
|
||||
require(eval_l == y_vals[i], "checkAndCommitAuxPolys: wrong lagrange poly");
|
||||
}
|
||||
// z(x) has len(x_vals) + 1 coeffs, we add to the commmitment the last coeff of z(x)
|
||||
z_commit = add(z_commit, mulScalar(G1_CRS[z_coeffs.length - 1], z_coeffs[z_coeffs.length - 1]));
|
||||
|
||||
return (z_commit, l_commit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Verifies a batch of point evaluation proofs. Function name follows `ark-poly`.
|
||||
* @dev To avoid ops in G_2, we slightly tweak how the verification is done.
|
||||
* @param c G1 point commitment to polynomial.
|
||||
* @param pi G2 point proof.
|
||||
* @param x_vals Values to prove evaluation of polynomial at.
|
||||
* @param y_vals Evaluation poly(x).
|
||||
* @param l_coeffs Coefficients of the lagrange polynomial.
|
||||
* @param z_coeffs Coefficients of the zero polynomial z(x) = (x - x_1)(x - x_2)...(x - x_n).
|
||||
* @return result Indicates if KZG proof is correct.
|
||||
*/
|
||||
function batchCheck(
|
||||
uint256[2] calldata c,
|
||||
uint256[2][2] calldata pi,
|
||||
uint256[] calldata x_vals,
|
||||
uint256[] calldata y_vals,
|
||||
uint256[] calldata l_coeffs,
|
||||
uint256[] calldata z_coeffs
|
||||
) public view returns (bool result) {
|
||||
//
|
||||
// we want to:
|
||||
// 1. avoid gas intensive ops in G2
|
||||
// 2. format the pairing check in line with what the evm opcode expects.
|
||||
//
|
||||
// we can do this by tweaking the KZG check to be:
|
||||
//
|
||||
// e(z(r) * g1, pi) * e(g1, l(r) * g2) = e(c, g2) [initial check]
|
||||
// e(z(r) * g1, pi) * e(l(r) * g1, g2) * e(c, g2)^{-1} = 1 [bilinearity of pairing]
|
||||
// e(z(r) * g1, pi) * e(l(r) * g1 - c, g2) = 1 [done]
|
||||
//
|
||||
(uint256[2] memory z_commit, uint256[2] memory l_commit) =
|
||||
checkAndCommitAuxPolys(z_coeffs, l_coeffs, x_vals, y_vals);
|
||||
uint256[2] memory neg_commit = negate(c);
|
||||
return pairing(z_commit, pi, add(l_commit, neg_commit), G_2);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user