def SameRatioSeqSym(sequence, pair): left = G * Fp(0) right = G * Fp(0) l = len(sequence) blinds = [random_fp() for i in range(l-1)] for i in range(l-1): left += sequence[i+1] * blinds[i] right += sequence[i] * blinds[i] return Group.pair(pair[0],left) == Group.pair(pair[1], right)
def vanishing_poly(S): """ args: S (m vector) returns: p(X) = (X-S1)*(X-S2)*...*(X-Sm) """ p = Poly([Fp(1)]) for s in S: p *= Poly([-s, Fp(1)]) return p
def eval_poly(poly, domain, shift=Fp(1)): poly_coeff = poly.to_coeffs() eval = [] for j in range(len(domain)): eval += [sum([(domain[j] * shift) ** i * poly_coeff.coefficients[i] for i in range(poly_coeff.degree()+1)])] return eval
def babysnarkopt_prover(U, n_stmt, CRS, precomp, a): (m, n) = U.shape assert n == len(a) assert len(CRS) == (m + 1) + 2 + (n - n_stmt) taus = CRS[: m + 1] bUis = CRS[-(n - n_stmt) :] Uis, T = precomp # Target is the vanishing polynomial mpow2 = m assert mpow2 & mpow2 - 1 == 0, "mpow2 must be a power of 2" omega = omega_base ** (2 ** 32 // mpow2) omega2 = omega_base ** (2 ** 32 // (2 * mpow2)) PolyEvalRep = polynomialsEvalRep(Fp, omega, mpow2) t = vanishing_poly(omega, mpow2) # 1. Find the polynomial p(X) # First compute v v = PolyEvalRep((), ()) for (i, k), y in U.items(): x = ROOTS[i] v += PolyEvalRep([x], [y]) * a[k] # Now we need to convert between representations to multiply and divide PolyEvalRep2 = polynomialsEvalRep(Fp, omega2, 2 * mpow2) roots2 = [omega2 ** i for i in range(2 * mpow2)] ONE = PolyEvalRep2(roots2, [Fp(1) for _ in roots2]) vv = v.to_coeffs() v2 = PolyEvalRep2.from_coeffs(v.to_coeffs()) p2 = v2 * v2 - ONE p = p2.to_coeffs() # Find the polynomial h by dividing p / t h = PolyEvalRep2.divideWithCoset(p, t) # assert p == h * t # 2. Compute the H term H = evaluate_in_exponent(taus, h) # 3. Compute the Vw terms, using precomputed Uis Vw = sum([Uis[k] * a[k] for k in range(n_stmt, n)], G * 0) # assert G * vw(tau) == Vw # 4. Compute the Bw terms Bw = sum([bUis[k - n_stmt] * a[k] for k in range(n_stmt, n)], G * 0) # assert G * (beta * vw(tau)) == Bw # V = G * vv(tau) # assert H.pair(T) * GT == V.pair(V) # print('H:', H) # print('Bw:', Bw) # print('Vw:', Vw) return H, Bw, Vw
def setup_algo(gates_matrix, permutation, L, p_i): print("Starting Setup Phase...") (m, n) = gates_matrix.shape assert n & n - 1 == 0, "n must be a power of 2" omega = omega_base ** (2 ** 32 // n) ROOTS = [omega ** i for i in range(n)] PolyEvalRep = polynomialsEvalRep(Fp, omega, n) # Generate polynomials from columns of gates_matrix q_L = PolyEvalRep(ROOTS, [Fp(i) for i in gates_matrix[0]]) q_R = PolyEvalRep(ROOTS, [Fp(i) for i in gates_matrix[1]]) q_M = PolyEvalRep(ROOTS, [Fp(i) for i in gates_matrix[2]]) q_O = PolyEvalRep(ROOTS, [Fp(i) for i in gates_matrix[3]]) q_C = PolyEvalRep(ROOTS, [Fp(i) for i in gates_matrix[4]]) Qs = [q_L, q_R, q_M, q_O, q_C] # The public input poly vanishes everywhere except for the position of the # public input gate where it evaluates to -(public_input) public_input = [Fp(0) for i in range(len(ROOTS))] for i in L: public_input[i] = Fp(-p_i) p_i_poly = PolyEvalRep(ROOTS, public_input) # We generate domains on which we can evaluate the witness polynomials k = random_fp() id_domain_a = ROOTS id_domain_b = [k * root for root in ROOTS] id_domain_c = [k**2 * root for root in ROOTS] id_domain = id_domain_a + id_domain_b + id_domain_c # We permute the positions of the domain generated above perm_domain = [id_domain[i - 1] for i in permutation] perm_domain_a = perm_domain[:n] perm_domain_b = perm_domain[n: 2*n] perm_domain_c = perm_domain[2*n:3*n] # Generate polynomials that return the permuted index when evaluated on the # domain S_sigma_1 = PolyEvalRep(ROOTS, perm_domain_a) S_sigma_2 = PolyEvalRep(ROOTS, perm_domain_b) S_sigma_3 = PolyEvalRep(ROOTS, perm_domain_c) Ss = [S_sigma_1, S_sigma_2, S_sigma_3] perm_precomp = [id_domain, perm_domain, k, Ss] # We perform the trusted setup tau = random_fp() CRS = [G * (tau ** i) for i in range(n + 3)] # We take some work off the shoulders of the verifier print("Starting Verifier Preprocessing...") q_exp = [evaluate_in_exponent(CRS, q.to_coeffs()) for q in Qs] s_exp = [evaluate_in_exponent(CRS, s.to_coeffs()) for s in Ss] x_exp = G2 * tau verifier_preprocessing = [q_exp, s_exp, x_exp] print("Setup Phase Finished!") return CRS, Qs, p_i_poly, perm_precomp, verifier_preprocessing
def _sparse_poly_demo(): PolyEvalRep = polynomialsEvalRep(Fp, omega, mpow2) # Example polynomial that has roots at most of the powers of omega xs = [omega ** 1, omega ** 4, omega ** 5] ys = [Fp(3), Fp(5), Fp(1)] f_rep = PolyEvalRep(xs, ys) for i in [0, 2, 3, 6, 7]: assert f_rep(omega ** i) == Fp(0) for i, x in enumerate(xs): assert f_rep(x) == ys[i] # Convert to coeffs and back f = f_rep.to_coeffs() assert f_rep.to_coeffs() == PolyEvalRep.from_coeffs(f).to_coeffs() # Check f and f_rep are consistent tau = random_fp() assert f(tau) == f_rep(tau)
def random_sparse_matrix(m, n, avgPerN=2): U = RowDictSparseMatrix(m, n, Fp(0)) # First fill the first column for row in range(m): U[row, 0] = random_fp() # Then pick randomly for the rest for _ in range(avgPerN * n - 1): row = random.randrange(m) col = random.randrange(n) U[row, col] = random_fp() return U
def accumulator_factor(n, i, witness, beta, id_domain, perm_domain, gamma): # This function is used in round 2 # i am doing permutation[j-1] below because the list starts at 0 and the # paper at 1 res = Fp(1) for j in range(i+1): nom_1 = witness[j] + beta * id_domain[j] + gamma denom_1 = witness[j] + beta * perm_domain[j] + gamma nom_2 = witness[n + j] + beta * id_domain[n+j] + gamma denom_2 = witness[n + j] + beta * perm_domain[n+j] + gamma nom_3 = witness[2 * n + j] + beta * id_domain[2*n+j] + gamma denom_3 = witness[2 * n + j] + beta * perm_domain[2*n+j] + gamma res *= (nom_1 / denom_1) * (nom_2 / denom_2) * (nom_3 / denom_3) return res
def vanishing_poly(omega, n): # For the special case of evaluating at all n powers of omega, # the vanishing poly has a special form. # t(X) = (X-1)(X-omega)....(X-omega^(n-1)) = X^n - 1 return Poly([Fp(-1)] + [Fp(0)] * (n - 1) + [Fp(1)])
def hash_to_fp(bytestr): assert type(bytestr) is bytes return Fp(int(sha256(bytestr).hexdigest(), 16)%Fp.p)
def random_fp(): return Fp(random.randint(0, Fp.p-1))
def verifier_algo(proof_SNARK, n, p_i_poly, verifier_preprocessing, k): print("Starting Verification...") omega = omega_base**(2**32 // n) first_output, second_output, third_output, fifth_output, fourth_output = proof_SNARK a_eval_exp, b_eval_exp, c_eval_exp = first_output z_eval_exp = second_output t_lo_eval_exp, t_mid_eval_exp, t_hi_eval_exp = third_output a_zeta, b_zeta, c_zeta, S_1_zeta, S_2_zeta, accumulator_shift_zeta, t_zeta, r_zeta = fourth_output W_zeta_eval_exp, W_zeta_omega_eval_exp = fifth_output q_exp, s_exp, x_exp = verifier_preprocessing q_L_exp, q_R_exp, q_M_exp, q_O_exp, q_C_exp = q_exp s_1_exp, s_2_exp, s_3_exp = s_exp print("Check1: Elements in group?") assert type(a_eval_exp) is SS_BLS12_381 assert type(b_eval_exp) is SS_BLS12_381 assert type(c_eval_exp) is SS_BLS12_381 assert type(z_eval_exp) is SS_BLS12_381 assert type(t_lo_eval_exp) is SS_BLS12_381 assert type(t_mid_eval_exp) is SS_BLS12_381 assert type(t_hi_eval_exp) is SS_BLS12_381 assert type(W_zeta_eval_exp) is SS_BLS12_381 assert type(W_zeta_omega_eval_exp) is SS_BLS12_381 print("Check2: Elements in field?") assert type(a_zeta) is Fp assert type(b_zeta) is Fp assert type(c_zeta) is Fp assert type(S_1_zeta) is Fp assert type(S_2_zeta) is Fp assert type(r_zeta) is Fp assert type(accumulator_shift_zeta) is Fp print("Check3: Public input in field?") assert type(p_i_poly) == polynomialsEvalRep(Fp, omega, n) print(type(p_i_poly)) print("Step4: Recompute challenges from transcript") beta = random_fp_seeded(str(first_output) + "0") gamma = random_fp_seeded(str(first_output) + "1") alpha = random_fp_seeded(str(first_output) + str(second_output)) zeta = random_fp_seeded( str(first_output) + str(second_output) + str(third_output)) nu = random_fp_seeded( str(first_output) + str(second_output) + str(third_output) + str(fourth_output)) u = random_fp_seeded(str(proof_SNARK)) print("Step5: Evaluate vanishing polynomial at zeta") vanishing_poly_eval = zeta**n - Fp(1) print("Step6: Evaluate lagrange polynomial at zeta") L_1_zeta = (zeta**n - Fp(1)) / (n * (zeta - Fp(1))) print("Step7: Evaluate public input polynomial at zeta") p_i_poly_zeta = eval_poly(p_i_poly, [zeta])[0] print("Step8: Compute quotient polynomial evaluation") t_zeta = (r_zeta + p_i_poly_zeta - (a_zeta + beta * S_1_zeta + gamma) * (b_zeta + beta * S_2_zeta + gamma) * (c_zeta + gamma) * accumulator_shift_zeta * alpha - L_1_zeta * alpha**2) / vanishing_poly_eval print("Step9: Comupte first part of batched polynomial commitment") D_1_exp = (q_M_exp * a_zeta * b_zeta * nu + q_L_exp * a_zeta * nu + q_R_exp * b_zeta * nu + q_O_exp * c_zeta * nu + q_C_exp * nu) D_1_exp += (z_eval_exp * ((a_zeta + beta * zeta + gamma) * (b_zeta + beta * k * zeta + gamma) * (c_zeta + beta * (k**2) * zeta + gamma) * alpha * nu + L_1_zeta * (alpha**2) * nu + u)) D_1_exp += (s_3_exp * (a_zeta + beta * S_1_zeta + gamma) * (b_zeta + beta * S_2_zeta + gamma) * alpha * beta * accumulator_shift_zeta * nu) * Fp(-1) print("Step10: Compute full batched polynomial commitment") F_1_exp = (t_lo_eval_exp + t_mid_eval_exp * zeta**(n + 2) + t_hi_eval_exp * zeta**(2 * (n + 2)) + D_1_exp + a_eval_exp * nu**2 + b_eval_exp * nu**3 + c_eval_exp * nu**4 + s_1_exp * nu**5 + s_2_exp * nu**6) print("Step 11: Compute group encoded batch evaluation") E_1_exp = G * (t_zeta + nu * r_zeta + nu**2 * a_zeta + nu**3 * b_zeta + nu**4 * c_zeta + nu**5 * S_1_zeta + nu**6 * S_2_zeta + u * accumulator_shift_zeta) print("Check12: Batch validate all evaluations via pairing") e11 = W_zeta_eval_exp + W_zeta_omega_eval_exp * u e21 = (W_zeta_eval_exp * zeta + W_zeta_omega_eval_exp * u * zeta * omega + F_1_exp + (E_1_exp * Fp(-1))) assert e11.pair(x_exp) == e21.pair(G2) print("Verification Successful!")
def random_fp_seeded(seeded): random.seed(seeded) return Fp(random.randint(0, Fp.p - 1))
#| we write an adaptor for it. See `py_ecc/` and `ssbls12.py` for details. from ssbls12 import Fp, Poly, Group G = Group.G GT = Group.GT #| ## Choosing the evaluation domain #| Define some canonical roots $r_1,...,r_m$. These are public parameters #| and can be set arbitrarily, and in particular they don't depend on the #| circuit (though there must be enough of them to represent the problem #| instance). # This is a global value. # Initializing it to 1,2,...,128 is enough for small examples in this file. # We'll overwrite it in `babysnark_setup` when a larger constraint system # is needed. And in `babysnark_opt.py` we'll define a different policy for # choosing roots that leads to FFT optimization. ROOTS = [Fp(i) for i in range(128)] #| Here we define the vanishing polynomial, which is a degree-$m$ polynomial #| that roots at the $m$ distinct locations given. def vanishing_poly(S): """ args: S (m vector) returns: p(X) = (X-S1)*(X-S2)*...*(X-Sm) """ p = Poly([Fp(1)]) for s in S: p *= Poly([-s, Fp(1)]) return p
def prover_algo(witness, CRS, Qs, p_i_poly, perm_precomp): print("Starting the Prover Algorithm") n = int(len(witness) / 3) assert n & n - 1 == 0, "n must be a power of 2" id_domain, perm_domain, k, Ss = perm_precomp # We need to convert between representations to multiply and divide more # efficiently. In round 3 we have to divide a polynomial of degree 4*n+6 # Not sure if there is a big benefit in keeping the lower order # representations or if it makes sense to do everything in the highest # order 8*n right away... # polys represented with n points omega = omega_base ** (2 ** 32 // n) ROOTS = [omega ** i for i in range(n)] PolyEvalRep = polynomialsEvalRep(Fp, omega, n) witness = [Fp(i) for i in witness] witness_poly_a = PolyEvalRep(ROOTS, witness[:n]) witness_poly_b = PolyEvalRep(ROOTS, witness[n:n*2]) witness_poly_c = PolyEvalRep(ROOTS, witness[2*n:]) vanishing_pol_coeff = vanishing_poly(omega, n) # polys represented with 2*n points omega2 = omega_base ** (2 ** 32 // (2 * n)) PolyEvalRep2 = polynomialsEvalRep(Fp, omega2, 2 * n) vanishing_poly_ext = PolyEvalRep2.from_coeffs(vanishing_pol_coeff) witness_poly_a_ext = PolyEvalRep2.from_coeffs(witness_poly_a.to_coeffs()) witness_poly_b_ext = PolyEvalRep2.from_coeffs(witness_poly_b.to_coeffs()) witness_poly_c_ext = PolyEvalRep2.from_coeffs(witness_poly_c.to_coeffs()) # polys represented with 8*n points omega3 = omega_base ** (2 ** 32 // (8 * n)) PolyEvalRep3 = polynomialsEvalRep(Fp, omega3, 8 * n) roots3 = [omega3 ** i for i in range(8 * n)] S1, S2, S3 = Ss S1_ext3 = PolyEvalRep3.from_coeffs(S1.to_coeffs()) S2_ext3 = PolyEvalRep3.from_coeffs(S2.to_coeffs()) S3_ext3 = PolyEvalRep3.from_coeffs(S3.to_coeffs()) p_i_poly_ext3 = PolyEvalRep3.from_coeffs(p_i_poly.to_coeffs()) qs_ext3 = [PolyEvalRep3.from_coeffs(q.to_coeffs()) for q in Qs] q_L_ext3, q_R_ext3, q_M_ext3, q_O_ext3, q_C_ext3 = qs_ext3 # Following the paper, we are using the Fiat Shamir Heuristic. We are # Simulating 5 rounds of communication with the verifier using a # random oracle for verifier answers print("Starting Round 1...") # Generate "random" blinding scalars rand_scalars = [random_fp_seeded("1234") for i in range(9)] # Generate polys with the random scalars as coefficients and convert to # evaluation representation. These are needed for zero knowledge to # obfuscate the witness. a_blind_poly_ext = Poly([rand_scalars[1], rand_scalars[0]]) b_blind_poly_ext = Poly([rand_scalars[3], rand_scalars[2]]) c_blind_poly_ext = Poly([rand_scalars[5], rand_scalars[4]]) a_blind_poly_ext = PolyEvalRep2.from_coeffs(a_blind_poly_ext) b_blind_poly_ext = PolyEvalRep2.from_coeffs(b_blind_poly_ext) c_blind_poly_ext = PolyEvalRep2.from_coeffs(c_blind_poly_ext) # These polynomals have random evaluations at all points except ROOTS where # they evaluate to the witness a_poly_ext = a_blind_poly_ext * vanishing_poly_ext + witness_poly_a_ext b_poly_ext = b_blind_poly_ext * vanishing_poly_ext + witness_poly_b_ext c_poly_ext = c_blind_poly_ext * vanishing_poly_ext + witness_poly_c_ext # Evaluate the witness polynomials in the exponent using the CRS a_eval_exp = evaluate_in_exponent(CRS, a_poly_ext.to_coeffs()) b_eval_exp = evaluate_in_exponent(CRS, b_poly_ext.to_coeffs()) c_eval_exp = evaluate_in_exponent(CRS, c_poly_ext.to_coeffs()) first_output = [a_eval_exp, b_eval_exp, c_eval_exp] print("Round 1 Finished with output: ", first_output) print("Starting Round 2...") # Compute permutation challenges from imaginary verifier beta = random_fp_seeded(str(first_output) + "0") gamma = random_fp_seeded(str(first_output) + "1") # Compute permutation polynomial. z_1 is the blinding summand needed for ZK z_1 = Poly([rand_scalars[8], rand_scalars[7], rand_scalars[6]]) z_1 = PolyEvalRep2.from_coeffs(z_1) z_1 = z_1 * vanishing_poly_ext accumulator_poly_eval = [Fp(1)] accumulator_poly_eval += [accumulator_factor(n, i, witness, beta, id_domain, perm_domain, gamma) for i in range(n-1)] accumulator_poly = PolyEvalRep(ROOTS, accumulator_poly_eval) accumulator_poly = z_1 + PolyEvalRep2.from_coeffs(accumulator_poly.to_coeffs()) second_output = evaluate_in_exponent(CRS, accumulator_poly.to_coeffs()) print("Round 2 Finished with output: ", second_output) print("Starting Round 3...") alpha = random_fp_seeded(str(first_output) + str(second_output)) accumulator_poly_ext3 = PolyEvalRep3.from_coeffs(accumulator_poly.to_coeffs()) # The third summand of t has the accumulator poly evaluated at a shift accumulator_poly_shift_evaluations = eval_poly(accumulator_poly, roots3, ROOTS[1]) accumulator_poly_ext3_shift = PolyEvalRep3(roots3, accumulator_poly_shift_evaluations) a_poly_ext3 = PolyEvalRep3.from_coeffs(a_poly_ext.to_coeffs()) b_poly_ext3 = PolyEvalRep3.from_coeffs(b_poly_ext.to_coeffs()) c_poly_ext3 = PolyEvalRep3.from_coeffs(c_poly_ext.to_coeffs()) id_poly_1_ext3 = PolyEvalRep3.from_coeffs(Poly([gamma, beta])) id_poly_2_ext3 = PolyEvalRep3.from_coeffs(Poly([gamma, beta * k])) id_poly_3_ext3 = PolyEvalRep3.from_coeffs(Poly([gamma, beta * k**2])) gamma_poly = PolyEvalRep3.from_coeffs(Poly([gamma])) L_1 = PolyEvalRep(ROOTS, [Fp(1)] + [Fp(0) for i in range(len(ROOTS)-1)]) # Compute quotient polynomial: we are dividing by the vanishing poly which # has zeros at n roots so we need to do division by swapping to a coset # first summand should have degree 3n+1, second and third should have # degree 4n + 5 t = ((a_poly_ext3 * b_poly_ext3 * q_M_ext3) + (a_poly_ext3 * q_L_ext3) + (b_poly_ext3 * q_R_ext3) + (c_poly_ext3 * q_O_ext3) + q_C_ext3 + p_i_poly_ext3) t += ((a_poly_ext3 + id_poly_1_ext3) * (b_poly_ext3 + id_poly_2_ext3) * (c_poly_ext3 + id_poly_3_ext3) * accumulator_poly_ext3 * alpha) t -= ((a_poly_ext3 + S1_ext3 * beta + gamma_poly) * (b_poly_ext3 + S2_ext3 * beta + gamma_poly) * (c_poly_ext3 + S3_ext3 * beta + gamma_poly) * accumulator_poly_ext3_shift * alpha) t += ((accumulator_poly_ext3 - PolyEvalRep3.from_coeffs(Poly([Fp(1)]))) * PolyEvalRep3.from_coeffs(L_1.to_coeffs()) * alpha ** 2) t = PolyEvalRep3.divideWithCoset(t.to_coeffs(), vanishing_pol_coeff) t_coeff = t.coefficients # We split up the polynomial t in three polynomials so that: # t= t_lo + x^n*t_mid + t^2n*t_hi # I found that n has actually to be (n+2) to accomodate the CRS because # t can be of degree 4n+5 t_lo = Poly(t_coeff[:n+2]) t_mid = Poly(t_coeff[n+2:2*(n+2)]) t_hi = Poly(t_coeff[2*(n+2):]) t_lo_eval_exp = evaluate_in_exponent(CRS, t_lo) t_mid_eval_exp = evaluate_in_exponent(CRS, t_mid) t_hi_eval_exp = evaluate_in_exponent(CRS, t_hi) third_output = [t_lo_eval_exp, t_mid_eval_exp, t_hi_eval_exp] print("Round 3 Finished with output: ", third_output) print("Starting Round 4...") # Compute the evaluation challenge zeta = random_fp_seeded(str(first_output) + str(second_output) + str(third_output)) # Compute the opening evaluations a_zeta = eval_poly(a_poly_ext, [zeta])[0] b_zeta = eval_poly(b_poly_ext, [zeta])[0] c_zeta = eval_poly(c_poly_ext, [zeta])[0] S_1_zeta = eval_poly(S1, [zeta])[0] S_2_zeta = eval_poly(S2, [zeta])[0] t_zeta = eval_poly(PolyEvalRep3.from_coeffs(t), [zeta])[0] accumulator_shift_zeta = eval_poly(accumulator_poly_ext3, [zeta * ROOTS[1]])[0] # Compute linerisation polynomial r = (q_M_ext3 * a_zeta * b_zeta + q_L_ext3 * a_zeta + q_R_ext3 * b_zeta + q_O_ext3 * c_zeta + q_C_ext3) r += (accumulator_poly_ext3 * (a_zeta + beta * zeta + gamma) * (b_zeta + beta * k * zeta + gamma) * (c_zeta + beta * (k ** 2) * zeta + gamma) * alpha) r -= (S3_ext3 * (a_zeta + beta * S_1_zeta + gamma) * (b_zeta + beta * S_2_zeta + gamma) * alpha * beta * accumulator_shift_zeta) r += accumulator_poly_ext3 * eval_poly(L_1, [zeta])[0] * alpha ** 2 # Evaluate r at zeta r_zeta = eval_poly(r, [zeta])[0] fourth_output = [a_zeta, b_zeta, c_zeta, S_1_zeta, S_2_zeta, accumulator_shift_zeta, t_zeta, r_zeta] print("Round 4 Finished with output: ", fourth_output) print("Starting Round 5...") # Compute opening challeng nu = random_fp_seeded(str(first_output) + str(second_output) + str(third_output) + str(fourth_output)) # Compute opening proof polynomial W_zeta = (PolyEvalRep3.from_coeffs(t_lo) + PolyEvalRep3.from_coeffs(t_mid) * zeta ** (n+2) + PolyEvalRep3.from_coeffs(t_hi) * zeta ** (2*(n+2)) - PolyEvalRep3.from_coeffs(Poly([t_zeta])) + (r - PolyEvalRep3.from_coeffs(Poly([r_zeta]))) * nu + (a_poly_ext3 - PolyEvalRep3.from_coeffs(Poly([a_zeta]))) * nu ** 2 + (b_poly_ext3 - PolyEvalRep3.from_coeffs(Poly([b_zeta]))) * nu ** 3 + (c_poly_ext3 - PolyEvalRep3.from_coeffs(Poly([c_zeta]))) * nu ** 4 + (S1_ext3 - PolyEvalRep3.from_coeffs(Poly([S_1_zeta]))) * nu ** 5 + (S2_ext3 - PolyEvalRep3.from_coeffs(Poly([S_2_zeta]))) * nu ** 6) W_zeta = W_zeta / PolyEvalRep3.from_coeffs(Poly([-zeta, Fp(1)])) # Compute the opening proof polynomial W_zeta_omega = accumulator_poly_ext3 - PolyEvalRep3.from_coeffs(Poly([accumulator_shift_zeta])) W_zeta_omega = W_zeta_omega / PolyEvalRep3.from_coeffs(Poly([-zeta*ROOTS[1], Fp(1)])) W_zeta_eval_exp = evaluate_in_exponent(CRS, W_zeta.to_coeffs()) W_zeta_omega_eval_exp = evaluate_in_exponent(CRS, W_zeta_omega.to_coeffs()) fifth_output = [W_zeta_eval_exp, W_zeta_omega_eval_exp] proof_SNARK = [first_output, second_output, third_output, fifth_output, fourth_output] print("Round 5 Finished with output: ", fifth_output) u = random_fp_seeded(str(proof_SNARK)) return proof_SNARK, u
def compile_to_solved_ssp(self, inp): # Evaluate the circuit to process the values of all gates # from the inputs self.evaluate(inp) # Preprocess the wires into the list of the desired form # We want all the wires for which we have inputs to be first # Recall a_vec = [1 a_stmt a_witness] a_vec = self.convert_wires_to_a_vec() # Store U in sparse form m = len(a_vec) - 1 + len(self.sorted_gates) m = nearestPowerOfTwo(m) n = len(a_vec) U = RowDictSparseMatrix(m, n) constraint_index = 0 for wire_index in range(1, len(a_vec)): wire_label = a_vec[wire_index] if wire_label not in self.statements_wires: # Put 0,1 constaint as a column # number of rows per column is len(wires) + 1 U[constraint_index, wire_index] = Fp(2) U[constraint_index, 0] = Fp(-1) else: # The wire must have some fixed value U[constraint_index, wire_index] = Fp(1) # force w_i to the correct value U[constraint_index, 0] = Fp(1 - self.wire_values[wire_label]) constraint_index = constraint_index + 1 # Now add constraints per type of gate for gid in self.sorted_gates: gate = self.gates[gid] inp1 = gate["inp"][0] ind_inp1 = self.get_index(inp1) out = gate["out"][0] ind_out = self.get_index(out) """ a + b - 2 + 2c in {0,1} 2a + 2b - 4 + 4c in {0,2} 2a + 2b + 4c - 5 in {-1,1} """ if gate["type"] == "NAND": inp2 = gate["inp"][1] ind_inp2 = self.get_index(inp2) U[constraint_index, ind_inp1] = Fp(2) U[constraint_index, ind_inp2] = Fp(2) U[constraint_index, ind_out] = Fp(4) U[constraint_index, 0] = Fp(-5) if gate["type"] == "INV": U[constraint_index, ind_inp1] = Fp(1) U[constraint_index, ind_out] = Fp(1) elif gate["type"] == "AND": inp2 = gate["inp"][1] ind_inp2 = self.get_index(inp2) U[constraint_index, ind_inp1] = Fp(2) U[constraint_index, ind_inp2] = Fp(2) U[constraint_index, ind_out] = Fp(-4) U[constraint_index, 0] = Fp(-1) elif gate["type"] == "OR": inp2 = gate["inp"][1] ind_inp2 = self.get_index(inp2) U[constraint_index, ind_inp1] = Fp(-2) U[constraint_index, ind_inp2] = Fp(-2) U[constraint_index, ind_out] = Fp(4) U[constraint_index, 0] = Fp(-1) elif gate["type"] == "XOR": inp2 = gate["inp"][1] ind_inp2 = self.get_index(inp2) U[constraint_index, ind_inp1] = Fp(1) U[constraint_index, ind_inp2] = Fp(1) U[constraint_index, ind_out] = Fp(1) U[constraint_index, 0] = Fp(-1) else: raise Exception( "We don't support any other gate than NAND/OR/AND/XOR/INV for now" ) constraint_index += 1 # Finally fill up the remaining spaces with dummy constraitns # of 1 = 1 while constraint_index < U.m: U[constraint_index, 0] = Fp(1) constraint_index += 1 # create the final witness with values in it # The first value is 1, others are according to the evaluated circuit a_final = [1] + [ self.wire_values[a_vec[i]] for i in range(1, len(a_vec)) ] assert len(a_final) == 1 + len(self.wire_values) # Returns a tuple with number of public inputs, a_vec and U return (1 + len(self.statements_wires), a_final, U)