From 7f5c4c2b41e3b43b8b7b9b08c4677823e5662f45 Mon Sep 17 00:00:00 2001 From: "yitang.lh" <liheact@gmail.com> Date: Wed, 26 Apr 2023 00:47:04 +0800 Subject: [PATCH 1/3] feat: finish Basic test --- setup.py | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index bb2b5ef..132089d 100644 --- a/setup.py +++ b/setup.py @@ -61,6 +61,16 @@ def from_file(cls, filename): # assert b.pairing(b.G2, powers_of_x[1]) == b.pairing(X2, b.G1) # print("X^1 points checked consistent") return cls(powers_of_x, X2) + + # Encodes the KZG commitment to the given polynomial coeffs + def coeffs_to_point(self, coeffs): + if len(coeffs) > len(self.powers_of_x): + raise Exception("Not enough powers in setup") + return ec_lincomb([(s, x) for s, x in zip(self.powers_of_x, coeffs)]) + + # Encodes the KZG commitment that evaluates to the given values in the group + def evaluations_to_point(self, evals): + return self.coeffs_to_point(evals.ifft().values) # Encodes the KZG commitment that evaluates to the given values in the group def commit(self, values: Polynomial) -> G1Point: @@ -69,9 +79,24 @@ def commit(self, values: Polynomial) -> G1Point: # Run inverse FFT to convert values from Lagrange basis to monomial basis # Optional: Check values size does not exceed maximum power setup can handle # Compute linear combination of setup with values - return NotImplemented + + return self.evaluations_to_point(values) # Generate the verification key for this program with the given setup def verification_key(self, pk: CommonPreprocessedInput) -> VerificationKey: # Create the appropriate VerificationKey object - return NotImplemented + + vk = VerificationKey( + Qm=self.evaluations_to_point(pk.QM), + Ql=self.evaluations_to_point(pk.QL), + Qr=self.evaluations_to_point(pk.QR), + Qo=self.evaluations_to_point(pk.QO), + Qc=self.evaluations_to_point(pk.QC), + S1=self.evaluations_to_point(pk.S1), + S2=self.evaluations_to_point(pk.S2), + S3=self.evaluations_to_point(pk.S3), + X_2=self.X2, + group_order=pk.group_order, + w=Scalar(Scalar.root_of_unity(pk.group_order)) + ) + return vk From e9597b062ff25bc37c0d19378d1c6f55ee9f8389 Mon Sep 17 00:00:00 2001 From: "yitang.lh" <liheact@gmail.com> Date: Mon, 15 May 2023 00:52:01 +0800 Subject: [PATCH 2/3] feat: finish prover --- prover.py | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 167 insertions(+), 8 deletions(-) diff --git a/prover.py b/prover.py index 927fc66..4c7649e 100644 --- a/prover.py +++ b/prover.py @@ -6,7 +6,6 @@ from transcript import Transcript, Message1, Message2, Message3, Message4, Message5 from poly import Polynomial, Basis - @dataclass class Proof: msg_1: Message1 @@ -99,10 +98,27 @@ def round_1( # - B_values: witness[program.wires()[i].R] # - C_values: witness[program.wires()[i].O] + A_values = [Scalar(0) for _ in range(group_order)] + B_values = [Scalar(0) for _ in range(group_order)] + C_values = [Scalar(0) for _ in range(group_order)] + + for i in range(len(program.wires())): + A_values[i] = Scalar(witness[program.wires()[i].L]) + B_values[i] = Scalar(witness[program.wires()[i].R]) + C_values[i] = Scalar(witness[program.wires()[i].O]) + # Construct A, B, C Lagrange interpolation polynomials for # A_values, B_values, C_values + self.A = Polynomial(A_values, Basis.LAGRANGE) + self.B = Polynomial(B_values, Basis.LAGRANGE) + self.C = Polynomial(C_values, Basis.LAGRANGE) # Compute a_1, b_1, c_1 commitments to A, B, C polynomials + a_1 = self.setup.evaluations_to_point(self.A) + b_1 = self.setup.evaluations_to_point(self.B) + c_1 = self.setup.evaluations_to_point(self.C) + + # Compute wire assignments # Sanity check that witness fulfils gate constraints assert ( @@ -129,6 +145,20 @@ def round_2(self) -> Message2: # self.rlc(val1, val2) = val_1 + self.beta * val_2 + gamma # Check that the last term Z_n = 1 + Z_values = [Scalar(1)] + roots_of_unity = Scalar.roots_of_unity(group_order) + print(roots_of_unity) + for i in range(group_order): + Z_values.append( + Z_values[-1] * + (self.A.values[i] + self.beta * roots_of_unity[i] + self.gamma) * + (self.B.values[i] + self.beta * 2 * roots_of_unity[i] + self.gamma) * + (self.C.values[i] + self.beta * 3 * roots_of_unity[i] + self.gamma) / + (self.A.values[i] + self.beta * self.pk.S1.values[i] + self.gamma) / + (self.B.values[i] + self.beta * self.pk.S2.values[i] + self.gamma) / + (self.C.values[i] + self.beta * self.pk.S3.values[i] + self.gamma) + ) + assert Z_values.pop() == 1 # Sanity-check that Z was computed correctly @@ -146,8 +176,9 @@ def round_2(self) -> Message2: ] == 0 # Construct Z, Lagrange interpolation polynomial for Z_values + self.Z = Polynomial(Z_values, Basis.LAGRANGE) # Cpmpute z_1 commitment to Z polynomial - + z_1 = self.setup.evaluations_to_point(self.Z) # Return z_1 return Message2(z_1) @@ -159,29 +190,49 @@ def round_3(self) -> Message3: # List of roots of unity at 4x fineness, i.e. the powers of µ # where µ^(4n) = 1 + self.quarter_roots = Scalar.roots_of_unity(group_order * 4) # Using self.fft_expand, move A, B, C into coset extended Lagrange basis + self.A_big = self.fft_expand(self.A) + self.B_big = self.fft_expand(self.B) + self.C_big = self.fft_expand(self.C) + # Z_H = X^N - 1, also in evaluation form in the coset + self.ZH_big = [ + ((Scalar(r) * self.fft_cofactor) ** group_order - 1) + for r in self.quarter_roots + ] # Expand public inputs polynomial PI into coset extended Lagrange + self.PI_big = self.fft_expand(self.PI) # Expand selector polynomials pk.QL, pk.QR, pk.QM, pk.QO, pk.QC # into the coset extended Lagrange basis + self.QL_big, self.QR_big, self.QM_big, self.QO_big, self.QC_big = \ + (self.fft_expand(x) for x in (self.pk.QL, self.pk.QR,self.pk.QM, self.pk.QO, self.pk.QC)) + # Expand permutation grand product polynomial Z into coset extended # Lagrange basis + self.Z_big = self.fft_expand(self.Z) # Expand shifted Z(ω) into coset extended Lagrange basis + Z_shifted_big = Polynomial(self.Z_big.values[4:] + self.Z_big.values[:4], Basis.LAGRANGE) + # Expand permutation polynomials pk.S1, pk.S2, pk.S3 into coset # extended Lagrange basis + self.S1_big = self.fft_expand(self.pk.S1) + self.S2_big = self.fft_expand(self.pk.S2) + self.S3_big = self.fft_expand(self.pk.S3) + # Compute Z_H = X^N - 1, also in evaluation form in the coset # Compute L0, the Lagrange basis polynomial that evaluates to 1 at x = 1 = ω^0 # and 0 at other roots of unity # Expand L0 into the coset extended Lagrange basis - L0_big = self.fft_expand( + self.L1_big = self.fft_expand( Polynomial([Scalar(1)] + [Scalar(0)] * (group_order - 1), Basis.LAGRANGE) ) @@ -201,6 +252,27 @@ def round_3(self) -> Message3: # (Z - 1) * L0 = 0 # L0 = Lagrange polynomial, equal at all roots of unity except 1 + QUOT_big = Polynomial([(( + self.A_big.values[i] * self.QL_big.values[i] + + self.B_big.values[i] * self.QR_big.values[i] + + self.A_big.values[i] * self.B_big.values[i] * self.QM_big.values[i] + + self.C_big.values[i] * self.QO_big.values[i] + + self.PI_big.values[i] + + self.QC_big.values[i] + ) + ( + (self.A_big.values[i] + self.beta * self.fft_cofactor * self.quarter_roots[i] + self.gamma) * + (self.B_big.values[i] + self.beta * 2 * self.fft_cofactor * self.quarter_roots[i] + self.gamma) * + (self.C_big.values[i] + self.beta * 3 * self.fft_cofactor * self.quarter_roots[i] + self.gamma) + ) * self.alpha * self.Z_big.values[i] - ( + (self.A_big.values[i] + self.beta * self.S1_big.values[i] + self.gamma) * + (self.B_big.values[i] + self.beta * self.S2_big.values[i] + self.gamma) * + (self.C_big.values[i] + self.beta * self.S3_big.values[i] + self.gamma) + ) * self.alpha * Z_shifted_big.values[i] + ( + (self.Z_big.values[i] - 1) * self.L1_big.values[i] * self.alpha**2 + )) / self.ZH_big[i] for i in range(group_order * 4)], Basis.LAGRANGE) + + all_coeffs = self.expanded_evals_to_coeffs(QUOT_big) + # Sanity check: QUOT has degree < 3n assert ( self.expanded_evals_to_coeffs(QUOT_big).values[-group_order:] @@ -211,16 +283,23 @@ def round_3(self) -> Message3: # Split up T into T1, T2 and T3 (needed because T has degree 3n - 4, so is # too big for the trusted setup) + self.T1 = Polynomial(all_coeffs.values[:group_order], Basis.MONOMIAL).fft() + self.T2 = Polynomial((all_coeffs.values[group_order: group_order * 2]), Basis.MONOMIAL).fft() + self.T3 = Polynomial((all_coeffs.values[group_order * 2: group_order * 3]), Basis.MONOMIAL).fft() + # Sanity check that we've computed T1, T2, T3 correctly assert ( - T1.barycentric_eval(fft_cofactor) - + T2.barycentric_eval(fft_cofactor) * fft_cofactor**group_order - + T3.barycentric_eval(fft_cofactor) * fft_cofactor ** (group_order * 2) + self.T1.barycentric_eval(self.fft_cofactor) + + self.T2.barycentric_eval(self.fft_cofactor) * self.fft_cofactor**group_order + + self.T3.barycentric_eval(self.fft_cofactor) * self.fft_cofactor ** (group_order * 2) ) == QUOT_big.values[0] print("Generated T1, T2, T3 polynomials") # Compute commitments t_lo_1, t_mid_1, t_hi_1 to T1, T2, T3 polynomials + t_lo_1 = self.setup.evaluations_to_point(self.T1) + t_mid_1 = self.setup.evaluations_to_point(self.T2) + t_hi_1 = self.setup.evaluations_to_point(self.T3) # Return t_lo_1, t_mid_1, t_hi_1 return Message3(t_lo_1, t_mid_1, t_hi_1) @@ -235,17 +314,44 @@ def round_4(self) -> Message4: # Compute s2_eval = pk.S2(zeta) # Compute z_shifted_eval = Z(zeta * ω) + roots_of_unity = Scalar.roots_of_unity(self.group_order) + + self.a_eval = self.A.barycentric_eval(self.zeta) + self.b_eval = self.B.barycentric_eval(self.zeta) + self.c_eval = self.C.barycentric_eval(self.zeta) + self.s1_eval = self.pk.S1.barycentric_eval(self.zeta) + self.s2_eval = self.pk.S2.barycentric_eval(self.zeta) + self.z_shifted_eval = self.Z.barycentric_eval(self.zeta * roots_of_unity[1]) + # Return a_eval, b_eval, c_eval, s1_eval, s2_eval, z_shifted_eval - return Message4(a_eval, b_eval, c_eval, s1_eval, s2_eval, z_shifted_eval) + return Message4(self.a_eval, self.b_eval, self.c_eval, self.s1_eval, self.s2_eval, self.z_shifted_eval) def round_5(self) -> Message5: + + group_order = self.group_order # Evaluate the Lagrange basis polynomial L0 at zeta # Evaluate the vanishing polynomial Z_H(X) = X^n - 1 at zeta + self.L1_eval = Polynomial([Scalar(1)] + [Scalar(0)] * (group_order - 1), Basis.LAGRANGE).barycentric_eval(self.zeta) + self.ZH_eval = self.zeta ** group_order - 1 + self.PI_eval = self.PI.barycentric_eval(self.zeta) # Move T1, T2, T3 into the coset extended Lagrange basis # Move pk.QL, pk.QR, pk.QM, pk.QO, pk.QC into the coset extended Lagrange basis # Move Z into the coset extended Lagrange basis # Move pk.S3 into the coset extended Lagrange basis + self.T1_big = self.fft_expand(self.T1) + self.T2_big = self.fft_expand(self.T2) + self.T3_big = self.fft_expand(self.T3) + + self.QL_big, self.QR_big, self.QM_big, self.QO_big, self.QC_big = ( + self.fft_expand(x) for x in (self.pk.QL, self.pk.QR,self.pk.QM, self.pk.QO, self.pk.QC) + ) + + self.Z_big = self.fft_expand(self.Z) + self.S3_big = self.fft_expand(self.pk.S3) + + + # Compute the "linearization polynomial" R. This is a clever way to avoid # needing to provide evaluations of _all_ the polynomials that we are @@ -262,15 +368,55 @@ def round_5(self) -> Message5: # replaced with their evaluations at Z, which do still need to be provided # Commit to R + R_big = Polynomial([( + self.a_eval * self.QL_big.values[i] + + self.b_eval * self.QR_big.values[i] + + self.a_eval * self.b_eval * self.QM_big.values[i] + + self.c_eval * self.QO_big.values[i] + + self.PI_eval + + self.QC_big.values[i] + ) + ( + (self.a_eval + self.beta * self.zeta + self.gamma) * + (self.b_eval + self.beta * 2 * self.zeta + self.gamma) * + (self.c_eval + self.beta * 3 * self.zeta + self.gamma) + ) * self.alpha * self.Z_big.values[i] - ( + (self.a_eval + self.beta * self.s1_eval + self.gamma) * + (self.b_eval + self.beta * self.s2_eval + self.gamma) * + (self.c_eval + self.beta * self.S3_big.values[i] + self.gamma) + ) * self.alpha * self.z_shifted_eval + ( + (self.Z_big.values[i] - 1) * self.L1_eval + ) * self.alpha**2 - ( + self.T1_big.values[i] + + self.zeta ** group_order * self.T2_big.values[i] + + self.zeta ** (group_order * 2) * self.T3_big.values[i] + ) * self.ZH_eval for i in range(4 * group_order)], Basis.LAGRANGE) + + R_coeffs = self.expanded_evals_to_coeffs(R_big) + R = Polynomial(R_coeffs.values[:group_order], Basis.MONOMIAL).fft() # Sanity-check R - assert R.barycentric_eval(zeta) == 0 + assert R.barycentric_eval(self.zeta) == 0 print("Generated linearization polynomial R") # Generate proof that W(z) = 0 and that the provided evaluations of # A, B, C, S1, S2 are correct + + # Generate proof that W(z) = 0 and that the provided evaluations of + # A, B, C, S1, S2 are correct + + W_z_big = Polynomial([( + R_big.values[i] + + self.v * (self.A_big.values[i] - self.a_eval) + + self.v**2 * (self.B_big.values[i] - self.b_eval) + + self.v**3 * (self.C_big.values[i] - self.c_eval) + + self.v**4 * (self.S1_big.values[i] - self.s1_eval) + + self.v**5 * (self.S2_big.values[i] - self.s2_eval) + ) / (self.fft_cofactor * self.quarter_roots[i] - self.zeta) for i in range(group_order * 4)], Basis.LAGRANGE) + + W_z_coeffs = self.expanded_evals_to_coeffs(W_z_big).values + # Move A, B, C into the coset extended Lagrange basis # Move pk.S1, pk.S2 into the coset extended Lagrange basis @@ -288,6 +434,8 @@ def round_5(self) -> Message5: assert W_z_coeffs[group_order:] == [0] * (group_order * 3) # Compute W_z_1 commitment to W_z + W_z = Polynomial(W_z_coeffs[:group_order], Basis.MONOMIAL).fft() + W_z_1 = self.setup.evaluations_to_point(W_z) # Generate proof that the provided evaluation of Z(z*w) is correct. This # awkwardly different term is needed because the permutation accumulator @@ -296,9 +444,20 @@ def round_5(self) -> Message5: # In other words: Compute W_zw = (Z - z_shifted_eval) / (X - zeta * ω) # Check that degree of W_z is not greater than n + roots_of_unity = Scalar.roots_of_unity(self.group_order) + + W_zw_big = Polynomial([ + (self.Z_big.values[i] - self.z_shifted_eval) / + (self.fft_cofactor * self.quarter_roots[i] - self.zeta * roots_of_unity[1]) + for i in range(group_order * 4)], Basis.LAGRANGE) + + W_zw_coeffs = self.expanded_evals_to_coeffs(W_zw_big).values + assert W_zw_coeffs[group_order:] == [0] * (group_order * 3) # Compute W_z_1 commitment to W_z + W_zw = Polynomial(W_zw_coeffs[:group_order], Basis.MONOMIAL).fft() + W_zw_1 = self.setup.evaluations_to_point(W_zw) print("Generated final quotient witness polynomials") From 88558ccc471d96b002b33a649b86edcefb0dfa4e Mon Sep 17 00:00:00 2001 From: "yitang.lh" <liheact@gmail.com> Date: Mon, 22 May 2023 16:21:02 +0800 Subject: [PATCH 3/3] feat: finish verifier --- prover.py | 4 ++ verifier.py | 159 +++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 154 insertions(+), 9 deletions(-) diff --git a/prover.py b/prover.py index 4c7649e..ee7204d 100644 --- a/prover.py +++ b/prover.py @@ -368,6 +368,10 @@ def round_5(self) -> Message5: # replaced with their evaluations at Z, which do still need to be provided # Commit to R + self.QL_big, self.QR_big, self.QM_big, self.QO_big, self.QC_big = ( + self.fft_expand(x) for x in (self.pk.QL, self.pk.QR,self.pk.QM, self.pk.QO, self.pk.QC) + ) + R_big = Polynomial([( self.a_eval * self.QL_big.values[i] + self.b_eval * self.QR_big.values[i] + diff --git a/verifier.py b/verifier.py index 4945e2f..894d939 100644 --- a/verifier.py +++ b/verifier.py @@ -4,6 +4,7 @@ from curve import * from transcript import Transcript from poly import Polynomial, Basis +from transcript import Transcript @dataclass @@ -38,19 +39,76 @@ class VerificationKey: # to understand and mixing together a lot of the computations to # efficiently batch them def verify_proof(self, group_order: int, pf, public=[]) -> bool: + + print("verify_proof") + # 4. Compute challenges + self.beta, self.gamma, self.alpha, self.zeta, self.v, self.u = self.compute_challenges(pf) # 5. Compute zero polynomial evaluation Z_H(ζ) = ζ^n - 1 - + self.ZH_eval = self.zeta ** group_order - 1 + # 6. Compute Lagrange polynomial evaluation L_0(ζ) - + self.L1_eval = Polynomial([Scalar(1)] + [Scalar(0)] * (group_order - 1), Basis.LAGRANGE).barycentric_eval(self.zeta) + # 7. Compute public input polynomial evaluation PI(ζ). + self.PI_eval = Polynomial([Scalar(-x) for x in public] + + [Scalar(0) for _ in range(group_order - len(public))], Basis.LAGRANGE).barycentric_eval(self.zeta) + + + flat_proof = pf.flatten() # Compute the constant term of R. This is not literally the degree-0 # term of the R polynomial; rather, it's the portion of R that can # be computed directly, without resorting to elliptic cutve commitments + r0 = ( + self.PI_eval - self.L1_eval * self.alpha ** 2 - ( + self.alpha * + (flat_proof["a_eval"] + self.beta * flat_proof["s1_eval"] + self.gamma) * + (flat_proof["b_eval"] + self.beta * flat_proof["s2_eval"] + self.gamma) * + (flat_proof["c_eval"] + self.gamma) * + flat_proof["z_shifted_eval"] + ) + ) + # Compute D = (R - r0) + u * Z, and E and F + D_pt = ec_lincomb([ + (self.Qm, flat_proof["a_eval"] * flat_proof["b_eval"]), + (self.Ql, flat_proof["a_eval"]), + (self.Qr, flat_proof["b_eval"]), + (self.Qo, flat_proof["c_eval"]), + (self.Qc, 1), + (flat_proof["z_1"], ( + (flat_proof["a_eval"] + self.beta * self.zeta + self.gamma) * + (flat_proof["b_eval"] + self.beta * 2 * self.zeta + self.gamma) * + (flat_proof["c_eval"] + self.beta * 3 * self.zeta + self.gamma) * self.alpha + + self.L1_eval * self.alpha ** 2 + + self.u + )), + (self.S3, ( + -(flat_proof["a_eval"] + self.beta * flat_proof["s1_eval"] + self.gamma) * + (flat_proof["b_eval"] + self.beta * flat_proof["s2_eval"] + self.gamma) * + self.alpha * self.beta * flat_proof["z_shifted_eval"] + )), + (flat_proof["t_lo_1"], -self.ZH_eval), + (flat_proof["t_mid_1"], -self.ZH_eval * self.zeta**group_order), + (flat_proof["t_hi_1"], -self.ZH_eval * self.zeta**(group_order*2)), + ]) + + F_pt = ec_lincomb([ + (D_pt, 1), + (flat_proof["a_1"], self.v), + (flat_proof["b_1"], self.v**2), + (flat_proof["c_1"], self.v**3), + (self.S1, self.v**4), + (self.S2, self.v**5), + ]) + + E_pt = ec_mul(b.G1, ( + -r0 + self.v * flat_proof["a_eval"] + self.v**2 * flat_proof["b_eval"] + self.v**3 * flat_proof["c_eval"] + + self.v**4 * flat_proof["s1_eval"] + self.v**5 * flat_proof["s2_eval"] + self.u * flat_proof["z_shifted_eval"] + )) # Run one pairing check to verify the last two checks. # What's going on here is a clever re-arrangement of terms to check @@ -68,28 +126,111 @@ def verify_proof(self, group_order: int, pf, public=[]) -> bool: # # so at this point we can take a random linear combination of the two # checks, and verify it with only one pairing. - - return False + + assert b.pairing(self.X_2, ec_lincomb([ + (flat_proof["W_z_1"], 1), + (flat_proof["W_zw_1"], self.u) + ])) == b.pairing(b.G2, ec_lincomb([ + (flat_proof["W_z_1"], self.zeta), + (flat_proof["W_zw_1"], self.u * self.zeta * self.w), + (F_pt, 1), + (E_pt, -1) + ])) + + print("verify_proof done combined check") + + return True # Basic, easier-to-understand version of what's going on - def verify_proof_unoptimized(self, group_order: int, pf, public=[]) -> bool: + def verify_proof_unoptimized(self, group_order: int, pf , public=[]) -> bool: + # 4. Compute challenges + self.beta, self.gamma, self.alpha, self.zeta, self.v, self.u = self.compute_challenges(pf) # 5. Compute zero polynomial evaluation Z_H(ζ) = ζ^n - 1 - + self.ZH_eval = self.zeta ** group_order - 1 + # 6. Compute Lagrange polynomial evaluation L_0(ζ) - + self.L1_eval = Polynomial([Scalar(1)] + [Scalar(0)] * (group_order - 1), Basis.LAGRANGE).barycentric_eval(self.zeta) + # 7. Compute public input polynomial evaluation PI(ζ). + self.PI_eval = Polynomial([Scalar(-x) for x in public] + + [Scalar(0) for _ in range(group_order - len(public))], Basis.LAGRANGE).barycentric_eval(self.zeta) + + flat_proof = pf.flatten() # Recover the commitment to the linearization polynomial R, # exactly the same as what was created by the prover + R = ec_lincomb([ + (self.Qm, flat_proof["a_eval"] * flat_proof["b_eval"]), + (self.Ql, flat_proof["a_eval"]), + (self.Qr, flat_proof["b_eval"]), + (self.Qo, flat_proof["c_eval"]), + (b.G1, self.PI_eval), + (self.Qc, 1), + (flat_proof["z_1"], ( + (flat_proof["a_eval"] + self.beta * self.zeta + self.gamma) * + (flat_proof["b_eval"] + self.beta * 2 * self.zeta + self.gamma) * + (flat_proof["c_eval"] + self.beta * 3 * self.zeta + self.gamma) * + self.alpha + )), + (self.S3, ( + -(flat_proof["a_eval"] + self.beta * flat_proof["s1_eval"] + self.gamma) * + (flat_proof["b_eval"] + self.beta * flat_proof["s2_eval"] + self.gamma) * + self.beta * + self.alpha * flat_proof["z_shifted_eval"] + )), + (b.G1, ( + -(flat_proof["a_eval"] + self.beta * flat_proof["s1_eval"] + self.gamma) * + (flat_proof["b_eval"] + self.beta * flat_proof["s2_eval"] + self.gamma) * + (flat_proof["c_eval"] + self.gamma) * + self.alpha * flat_proof["z_shifted_eval"] + )), + (flat_proof["z_1"], self.L1_eval * self.alpha ** 2), + (b.G1, -self.L1_eval * self.alpha ** 2), + (flat_proof["t_lo_1"], -self.ZH_eval), + (flat_proof["t_mid_1"], -self.ZH_eval * self.zeta**group_order), + (flat_proof["t_hi_1"], -self.ZH_eval * self.zeta**(group_order*2)), + ]) + print('verify_proof_unoptimized verifier R_pt', R) # Verify that R(z) = 0 and the prover-provided evaluations # A(z), B(z), C(z), S1(z), S2(z) are all correct + assert b.pairing( + b.G2, + ec_lincomb([ + (R, 1), + (flat_proof["a_1"], self.v), + (b.G1, -self.v * flat_proof["a_eval"]), + (flat_proof["b_1"], self.v**2), + (b.G1, -self.v**2 * flat_proof["b_eval"]), + (flat_proof["c_1"], self.v**3), + (b.G1, -self.v**3 * flat_proof["c_eval"]), + (self.S1, self.v**4), + (b.G1, -self.v**4 * flat_proof["s1_eval"]), + (self.S2, self.v**5), + (b.G1, -self.v**5 * flat_proof["s2_eval"]), + ]) + ) == b.pairing( + b.add(self.X_2, ec_mul(b.G2, -self.zeta)), + flat_proof["W_z_1"] + ) + print("verify_proof_unoptimized done check 1") # Verify that the provided value of Z(zeta*w) is correct - - return False + assert b.pairing( + b.G2, + ec_lincomb([ + (flat_proof["z_1"], 1), + (b.G1, -flat_proof["z_shifted_eval"]) + ]) + ) == b.pairing( + b.add(self.X_2, ec_mul(b.G2, -self.zeta * self.w)), + flat_proof["W_zw_1"] + ) + print("verify_proof_unoptimized done check 2") + + return True # Compute challenges (should be same as those computed by prover) def compute_challenges(