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 babysnark_prover(U, n_stmt, CRS, precomp, a): (m, n) = U.shape assert n == len(a) assert len(CRS) == (m + 1) + 2 + (n - n_stmt) assert len(ROOTS) >= m # Parse the CRS taus = CRS[:m + 1] bUis = CRS[-(n - n_stmt):] Uis, T = precomp # Target is the vanishing polynomial t = vanishing_poly(ROOTS[:m]) # 1. Find the polynomial p(X) # Convert the basis polynomials Us to coefficient form by interpolating Us = [Poly.interpolate(ROOTS[:m], U[:, k]) for k in range(n)] # First compute v v = Poly([]) for k in range(n): v += Us[k] * a[k] # Finally p p = v * v - 1 # 2. Compute the H term # Find the polynomial h by dividing p / t h = p / t # assert p == h * t 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 * v(tau) # assert H.pair(T) * GT == V.pair(V) # print('H:', H) # print('Bw:', Bw) # print('Vw:', Vw) return H, Bw, Vw
def babysnark_setup(U, n_stmt): (m, n) = U.shape assert n_stmt < n # Generate roots for each gate global ROOTS if len(ROOTS) < m: ROOTS = tuple(range(m)) # Generate polynomials u from columns of U Us = [Poly.interpolate(ROOTS[:m], U[:, k]) for k in range(n)] # Trapdoors global tau, beta, gamma tau = random_fp() beta = random_fp() gamma = random_fp() # CRS elements CRS = [G * (tau ** i) for i in range(m+1)] + \ [G * gamma, G * (beta * gamma)] + \ [G * (beta * Ui(tau)) for Ui in Us[n_stmt:]] # Precomputation # Note: This is not considered part of the trusted setup, since it # could be computed direcftly from the G * (tau **i) terms. # Compute the target poly term t = vanishing_poly(ROOTS[:m]) T = G * t(tau) # Evaluate the Ui's corresponding to statement values Uis = [G * Ui(tau) for Ui in Us] precomp = Uis, T return CRS, precomp
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 test_two_round(crstype): numr1players = 6 numr2players = 6 numpolys = 3 crslen = 5 n = numr1players + numr2players + 1 numplayers = n - 1 sends, recvs = get_routing(n) bcfuncs = gen_blockchain_funcs(sends, recvs) loop = asyncio.get_event_loop() public = {} if crstype == "BabySNARK": #generate a random alpha, gamma for each party trapdoors1 = [[random_fp() for j in range(2)] for i in range(numr1players + 1)] #generate a random beta for round 2 players trapdoors2 = [random_fp() for i in range(numr2players)] trapdoors = trapdoors1 + trapdoors2 polys = [ Poly([random_fp() for j in range(crslen)]) for i in range(numpolys) ] public['polys'] = polys crs = init_babysnark(trapdoors[0], crslen) TestPlayer1 = BabySNARKr1Player TestPlayer2 = BabySNARKr2Player bc = Blockchain(sends[0], recvs[0], beacon=beacon_babysnark) #A proof is unnecessary for the first block as trapdoors are public pi = None #set the original CRS sends[0](0, ["SET_NOWAIT", [pi, crs]]) bctask = loop.create_task(bc.run()) mysleep = loop.create_task(asyncio.sleep(.5)) loop.run_until_complete(mysleep) options = [{'roles': ['contributor']} for i in range(numplayers)] players = [None] * numplayers r1players = [ TestPlayer1(i, trapdoors[i], sends[i], recvs[i], bcfuncs[i], options[i - 1]) for i in range(1, numr1players + 1) ] r2players = [ TestPlayer2(i, trapdoors[i], sends[i], recvs[i], bcfuncs[i], options[i - 1], public) for i in range(numr1players + 1, numplayers + 1) ] r1players[numr1players // 2].roles += ["validator"] r2players[numr2players // 2].roles += ["validator"] r2players[0].roles += ["roundstarter"] r1players[-1].roles += ["roundender"] r2players[-1].roles += ["roundender"] players = r1players + r2players for i in range(3, numplayers, 3): players[i].roles += ["checkpoint"] #send starting message to first player sends[0](1, ["INC", None, crs]) playertasks = [loop.create_task(player.run()) for player in players] for task in [bctask] + playertasks: task.add_done_callback(print_exception_callback) loop.run_until_complete(playertasks[-1]) get_block = bcfuncs[1][1] mytask = loop.create_task(get_block("ALL")) blocks = loop.run_until_complete(mytask) assert TestPlayer2.verify_chain(blocks, public) bctask.cancel()
def _polydemo(): p1 = Poly([1, 2, 3, 4, 5])
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