diff --git a/eth_verifier/lib/Commitment.sol b/eth_verifier/lib/Commitment.sol index 9fc1de00..cbf05c15 100644 --- a/eth_verifier/lib/Commitment.sol +++ b/eth_verifier/lib/Commitment.sol @@ -52,9 +52,9 @@ function random_lagrange_bases(PairingURS storage urs, uint256 domain_size) { // WARN: The field shifted is optional but in Solidity we can't have that. // for our test circuit it's not necessary, we can just ignore it, using infinity. -//BN254.G1Point shifted; struct PolyComm { BN254.G1Point[] unshifted; + BN254.G1Point shifted; } // @notice this structure flattens the fields of `PolyComm`. @@ -76,7 +76,9 @@ function poly_comm_unflat(PolyCommFlat memory com) pure returns (PolyComm[] memo unshifted[j] = com.unshifteds[index]; index++; } - res[i] = PolyComm(unshifted); + // TODO: shifted is fixed to infinity + BN254.G1Point memory shifted = BN254.point_at_inf(); + res[i] = PolyComm(unshifted, shifted); } } @@ -106,7 +108,9 @@ function polycomm_msm(PolyComm[] memory com, Scalar.FE[] memory elm) view return if (com.length == 0 || elm.length == 0) { BN254.G1Point[] memory z = new BN254.G1Point[](1); z[0] = BN254.point_at_inf(); - return PolyComm(z); + // TODO: shifted is fixed to infinity + BN254.G1Point memory shifted = BN254.point_at_inf(); + return PolyComm(z, shifted); } if (com.length != elm.length) { @@ -150,7 +154,9 @@ function polycomm_msm(PolyComm[] memory com, Scalar.FE[] memory elm) view return BN254.G1Point memory chunk_msm = naive_msm(points, scalars); unshifted[chunk] = chunk_msm; } - return PolyComm(unshifted); + // TODO: shifted is fixed to infinity + BN254.G1Point memory shifted = BN254.point_at_inf(); + return PolyComm(unshifted, shifted); } // @notice Execute a simple multi-scalar multiplication @@ -189,7 +195,9 @@ function mask_custom(URS storage urs, PolyComm memory com, Scalar.FE[] memory bl unshifted[i] = (g_masked.add(com.unshifted[i])); } - return BlindedCommitment(PolyComm(unshifted), blinders); + // TODO: shifted is fixed to infinity + BN254.G1Point memory shifted = BN254.point_at_inf(); + return BlindedCommitment(PolyComm(unshifted, shifted), blinders); } // @notice multiplies each commitment chunk of f with powers of zeta^n @@ -208,7 +216,9 @@ function chunk_commitment(PolyComm memory self, Scalar.FE zeta_n) view returns ( BN254.G1Point[] memory unshifted = new BN254.G1Point[](1); unshifted[0] = res; - return PolyComm(unshifted); + // TODO: shifted is fixed to infinity + BN254.G1Point memory shifted = BN254.point_at_inf(); + return PolyComm(unshifted, shifted); } // @notice substracts two polynomial commitments diff --git a/eth_verifier/lib/Evaluations.sol b/eth_verifier/lib/Evaluations.sol index 6e67d71c..191f42de 100644 --- a/eth_verifier/lib/Evaluations.sol +++ b/eth_verifier/lib/Evaluations.sol @@ -2,6 +2,8 @@ pragma solidity >=0.4.16 <0.9.0; import "./bn254/Fields.sol"; +import "./Polynomial.sol"; +import "./Commitment.sol"; struct PointEvaluations { /// evaluation at the challenge point zeta @@ -16,3 +18,13 @@ struct PointEvaluationsArray { /// Evaluation at `zeta . omega`, the product of the challenge point and the group generator Scalar.FE[] zeta_omega; } + +/// Contains the evaluation of a polynomial commitment at a set of points. +struct Evaluation { + /// The commitment of the polynomial being evaluated + PolyComm commitment; // TODO: Dense + /// Contains an evaluation table + Scalar.FE[][] evaluations; + /// optional degree bound + uint128 degree_bound; +} diff --git a/eth_verifier/lib/msgpack/Deserialize.sol b/eth_verifier/lib/msgpack/Deserialize.sol index 4cdedfcc..fa164c02 100644 --- a/eth_verifier/lib/msgpack/Deserialize.sol +++ b/eth_verifier/lib/msgpack/Deserialize.sol @@ -380,9 +380,9 @@ library MsgPk { EncodedMap memory buffer = abi.decode(unshifted_arr.values[i], (EncodedMap)); unshifted[i] = BN254.g1Deserialize(bytes32(deser_buffer(buffer))); } - // TODO: shifted part - - return PolyComm(unshifted); + // TODO: shifted is fixed to infinity + BN254.G1Point memory shifted = BN254.point_at_inf(); + return PolyComm(unshifted, shifted); } function deser_prover_proof(Stream memory self, ProverProof storage prover_proof) external { @@ -509,7 +509,9 @@ library MsgPk { unshifted[k] = BN254.g1Deserialize(bytes32(deser_buffer(unshifted_buffer))); } - polycomms[j] = PolyComm(unshifted); + // TODO: shifted is fixed to infinity + BN254.G1Point memory shifted = BN254.point_at_inf(); + polycomms[j] = PolyComm(unshifted, shifted); } lagrange_bases_unshifted[abi.decode(map.keys[i], (uint256))] = poly_comm_flat(polycomms); diff --git a/eth_verifier/src/Verifier.sol b/eth_verifier/src/Verifier.sol index 1151d48f..688f847f 100644 --- a/eth_verifier/src/Verifier.sol +++ b/eth_verifier/src/Verifier.sol @@ -18,7 +18,7 @@ import "../lib/expr/PolishToken.sol"; import "../lib/expr/ExprConstants.sol"; using {BN254.neg} for BN254.G1Point; -using {Scalar.neg, Scalar.mul, Scalar.add, Scalar.pow} for Scalar.FE; +using {Scalar.neg, Scalar.mul, Scalar.add, Scalar.inv, Scalar.sub, Scalar.pow} for Scalar.FE; using {AlphasLib.get_alphas} for Alphas; using {Polynomial.evaluate} for Polynomial.Dense; @@ -152,7 +152,9 @@ contract KimchiVerifier { for (uint256 i = 0; i < chunk_size; i++) { blindings[i] = urs.full_urs.h; } - public_comm = PolyComm(blindings); + // TODO: shifted is fixed to infinity + BN254.G1Point memory shifted = BN254.point_at_inf(); + public_comm = PolyComm(blindings, shifted); } else { Scalar.FE[] memory elm = new Scalar.FE[](public_inputs.length); for (uint256 i = 0; i < elm.length; i++) { @@ -246,6 +248,104 @@ contract KimchiVerifier { } } + /// The polynomial that evaluates to each of `evals` for the respective `elm`s. + function evalPolynomial(Scalar.FE[] memory elm, Scalar.FE[] memory evals) + public + view + returns (Polynomial.Dense memory) + { + require(elm.length == evals.length, "lengths don\'t match"); + require(elm.length == 2, "length must be 2"); + Scalar.FE zeta = elm[0]; + Scalar.FE zeta_omega = elm[1]; + Scalar.FE eval_zeta = evals[0]; + Scalar.FE eval_zeta_omega = evals[1]; + + // The polynomial that evaluates to `p(zeta)` at `zeta` and `p(zeta_omega)` at + // `zeta_omega`. + // We write `p(x) = a + bx`, which gives + // ```text + // p(zeta) = a + b * zeta + // p(zeta_omega) = a + b * zeta_omega + // ``` + // and so + // ```text + // b = (p(zeta_omega) - p(zeta)) / (zeta_omega - zeta) + // a = p(zeta) - b * zeta + // ``` + + // Compute b + Scalar.FE num_b = eval_zeta_omega.add(eval_zeta.neg()); + Scalar.FE den_b_inv = zeta_omega.add(zeta.neg()).inv(); + Scalar.FE b = num_b.mul(den_b_inv); + + // Compute a + Scalar.FE a = eval_zeta.sub(b.mul(zeta)); + + Scalar.FE[] memory coeffs = new Scalar.FE[](2); + coeffs[0] = a; + coeffs[1] = b; + return Polynomial.Dense(coeffs); + } + + function combineCommitments(Evaluation[] memory evaluations, Scalar.FE polyscale, Scalar.FE rand_base) + internal + returns (Scalar.FE[] memory) + { + uint256 vec_length = 0; + // Calculate the max length of the points and scalars vectors + // Iterate over the evaluations + for (uint256 i = 0; i < evaluations.length; i++) { + // Filter out evaluations with an empty commitment + if (evaluations[i].commitment.unshifted.length == 0) { + continue; + } + + vec_length += evaluations[i].commitment.unshifted.length + 1; + } + BN254.G1Point[] memory points = new BN254.G1Point[](vec_length); + Scalar.FE[] memory scalars = new Scalar.FE[](vec_length); + uint256 index = 0; // index of the element to assign in the vectors + + // Initialize xi_i to 1 + Scalar.FE xi_i = Scalar.FE.wrap(1); + + // Iterate over the evaluations + for (uint256 i = 0; i < evaluations.length; i++) { + // Filter out evaluations with an empty commitment + if (evaluations[i].commitment.unshifted.length == 0) { + continue; + } + + // iterating over the polynomial segments + for (uint256 j = 0; j < evaluations[i].commitment.unshifted.length; j++) { + // Add the scalar rand_base * xi_i to the scalars vector + scalars[index] = rand_base.mul(xi_i); + // Add the point to the points vector + points[index] = evaluations[i].commitment.unshifted[j]; + + // Multiply xi_i by polyscale + xi_i = xi_i.mul(polyscale); + + // Increment the index + index++; + } + + // If the evaluation has a degree bound and a non-zero shifted commitment + if (evaluations[i].degree_bound > 0 && evaluations[i].commitment.shifted.x != 0) { + // Add the scalar rand_base * xi_i to the scalars vector + scalars[index] = rand_base.mul(xi_i); + // Add the point to the points vector + points[index] = evaluations[i].commitment.shifted; + + // Multiply xi_i by polyscale + xi_i = xi_i.mul(polyscale); + // Increment the index + index++; + } + } + } + /* This is a list of steps needed for verification. @@ -267,6 +367,54 @@ contract KimchiVerifier { 6. Check numerator == scaled_quotient */ + function final_verify(Scalar.FE[] memory public_inputs) public { + /* + pub fn verify( + &self, + srs: &PairingSRS, // SRS + evaluations: &Vec>, // commitments to the polynomials + polyscale: G::ScalarField, // scaling factor for polynoms + elm: &[G::ScalarField], // vector of evaluation points + ) -> bool { + let poly_commitment = { + let mut scalars: Vec = Vec::new(); + let mut points = Vec::new(); + combine_commitments( + evaluations, + &mut scalars, + &mut points, + polyscale, + F::one(), // TODO: This is inefficient + ); + let scalars: Vec<_> = scalars.iter().map(|x| x.into_repr()).collect(); + + VariableBaseMSM::multi_scalar_mul(&points, &scalars) + }; + + let evals = combine_evaluations(evaluations, polyscale); + let blinding_commitment = srs.full_srs.h.mul(self.blinding); + let divisor_commitment = srs + .verifier_srs + .commit_non_hiding(&divisor_polynomial(elm), 1, None) + .unshifted[0]; + + let eval_commitment = srs + .full_srs + .commit_non_hiding(&eval_polynomial(elm, &evals), 1, None) + .unshifted[0] + .into_projective(); + let numerator_commitment = { poly_commitment - eval_commitment - blinding_commitment }; + + let numerator = Pair::pairing( + numerator_commitment, + Pair::G2Affine::prime_subgroup_generator(), + ); + let scaled_quotient = Pair::pairing(self.quotient, divisor_commitment); + numerator == scaled_quotient + } + */ + } + /* TODO WIP function deserialize_proof( uint256[] calldata public_inputs,