def test_fri(): # Pure FRI tests poly = list(range(4096)) root_of_unity = pow(7, (modulus - 1) // 16384, modulus) evaluations = fft(poly, modulus, root_of_unity) proof = prove_low_degree(evaluations, root_of_unity, 4096, modulus) print("Approx proof length: %d" % bin_length(compress_fri(proof))) assert verify_low_degree_proof( merkelize(evaluations)[1], root_of_unity, proof, 4096, modulus) try: fakedata = [ x if pow(3, i, 4096) > 400 else 39 for x, i in enumerate(evaluations) ] proof2 = prove_low_degree(fakedata, root_of_unity, 4096, modulus) assert verify_low_degree_proof( merkelize(fakedata)[1], root_of_unity, proof, 4096, modulus) raise Exception("Fake data passed FRI") except: pass try: assert verify_low_degree_proof( merkelize(evaluations)[1], root_of_unity, proof, 2048, modulus) raise Exception("Fake data passed FRI") except: pass
def mk_mimc_proof(inp, steps, round_constants): start_time = time.time() # Some constraints to make our job easier assert steps <= 2**32 // extension_factor assert is_a_power_of_2(steps) and is_a_power_of_2(len(round_constants)) assert len(round_constants) < steps precision = steps * extension_factor # Root of unity such that x^precision=1 G2 = f.exp(7, (modulus-1)//precision) # Root of unity such that x^steps=1 skips = precision // steps G1 = f.exp(G2, skips) # Powers of the higher-order root of unity xs = get_power_cycle(G2, modulus) last_step_position = xs[(steps-1)*extension_factor] # Generate the computational trace computational_trace = [inp] for i in range(steps-1): computational_trace.append( (computational_trace[-1]**3 + round_constants[i % len(round_constants)]) % modulus ) output = computational_trace[-1] print('Done generating computational trace') # Interpolate the computational trace into a polynomial P, with each step # along a successive power of G1 computational_trace_polynomial = fft(computational_trace, modulus, G1, inv=True) p_evaluations = fft(computational_trace_polynomial, modulus, G2) print('Converted computational steps into a polynomial and low-degree extended it') skips2 = steps // len(round_constants) constants_mini_polynomial = fft(round_constants, modulus, f.exp(G1, skips2), inv=True) constants_polynomial = [0 if i % skips2 else constants_mini_polynomial[i//skips2] for i in range(steps)] constants_mini_extension = fft(constants_mini_polynomial, modulus, f.exp(G2, skips2)) print('Converted round constants into a polynomial and low-degree extended it') # Create the composed polynomial such that # C(P(x), P(g1*x), K(x)) = P(g1*x) - P(x)**3 - K(x) c_of_p_evaluations = [(p_evaluations[(i+extension_factor)%precision] - f.exp(p_evaluations[i], 3) - constants_mini_extension[i % len(constants_mini_extension)]) % modulus for i in range(precision)] print('Computed C(P, K) polynomial') # Compute D(x) = C(P(x), P(g1*x), K(x)) / Z(x) # Z(x) = (x^steps - 1) / (x - x_atlast_step) z_num_evaluations = [xs[(i * steps) % precision] - 1 for i in range(precision)] z_num_inv = f.multi_inv(z_num_evaluations) z_den_evaluations = [xs[i] - last_step_position for i in range(precision)] d_evaluations = [cp * zd * zni % modulus for cp, zd, zni in zip(c_of_p_evaluations, z_den_evaluations, z_num_inv)] print('Computed D polynomial') # Compute interpolant of ((1, input), (x_atlast_step, output)) interpolant = f.lagrange_interp_2([1, last_step_position], [inp, output]) i_evaluations = [f.eval_poly_at(interpolant, x) for x in xs] zeropoly2 = f.mul_polys([-1, 1], [-last_step_position, 1]) inv_z2_evaluations = f.multi_inv([f.eval_poly_at(zeropoly2, x) for x in xs]) b_evaluations = [((p - i) * invq) % modulus for p, i, invq in zip(p_evaluations, i_evaluations, inv_z2_evaluations)] print('Computed B polynomial') # Compute their Merkle root mtree = merkelize([pval.to_bytes(32, 'big') + dval.to_bytes(32, 'big') + bval.to_bytes(32, 'big') for pval, dval, bval in zip(p_evaluations, d_evaluations, b_evaluations)]) print('Computed hash root') # Based on the hashes of P, D and B, we select a random linear combination # of P * x^steps, P, B * x^steps, B and D, and prove the low-degreeness of that, # instead of proving the low-degreeness of P, B and D separately k1 = int.from_bytes(blake(mtree[1] + b'\x01'), 'big') k2 = int.from_bytes(blake(mtree[1] + b'\x02'), 'big') k3 = int.from_bytes(blake(mtree[1] + b'\x03'), 'big') k4 = int.from_bytes(blake(mtree[1] + b'\x04'), 'big') # Compute the linear combination. We don't even both calculating it in # coefficient form; we just compute the evaluations G2_to_the_steps = f.exp(G2, steps) powers = [1] for i in range(1, precision): powers.append(powers[-1] * G2_to_the_steps % modulus) l_evaluations = [(d_evaluations[i] + p_evaluations[i] * k1 + p_evaluations[i] * k2 * powers[i] + b_evaluations[i] * k3 + b_evaluations[i] * powers[i] * k4) % modulus for i in range(precision)] l_mtree = merkelize(l_evaluations) print('Computed random linear combination') # Do some spot checks of the Merkle tree at pseudo-random coordinates, excluding # multiples of `extension_factor` branches = [] samples = spot_check_security_factor positions = get_pseudorandom_indices(l_mtree[1], precision, samples, exclude_multiples_of=extension_factor) augmented_positions = sum([[x, (x + skips) % precision] for x in positions], []) #for pos in positions: # branches.append(mk_branch(mtree, pos)) # branches.append(mk_branch(mtree, (pos + skips) % precision)) # branches.append(mk_branch(l_mtree, pos)) print('Computed %d spot checks' % samples) # Return the Merkle roots of P and D, the spot check Merkle proofs, # and low-degree proofs of P and D o = [mtree[1], l_mtree[1], mk_multi_branch(mtree, augmented_positions), mk_multi_branch(l_mtree, positions), prove_low_degree(l_evaluations, G2, steps * 2, modulus, exclude_multiples_of=extension_factor)] print("STARK computed in %.4f sec" % (time.time() - start_time)) return o