Files
darkfi/script/research/groth16/qap.py
2021-09-16 12:07:51 +02:00

180 lines
3.3 KiB
Python

import numpy as np
# Lets prove we know the answer to x**3 + x + 5 == 35 (x = 5)
# We break it down into these statements:
# L1: s1 = x * x
# L2: y = s1 * x
# L3: s2 = y + x
# L4: out = s2 + 5
# Statements are of the form:
# a * b = c
# s1 = x * x
# OR a * b = c, where a = x, b = x and c = s1
L1 = np.array([
# a b c
[0, 0, 0], # 1
[1, 1, 0], # x
[0, 0, 0], # out
[0, 0, 1], # s1
[0, 0, 0], # y
[0, 0, 0] # s2
])
# y = s1 * x
L2 = np.array([
# a b c
[0, 0, 0], # 1
[0, 1, 0], # x
[0, 0, 0], # out
[1, 0, 0], # s1
[0, 0, 1], # y
[0, 0, 0] # s2
])
# s2 = y + x
L3 = np.array([
# a b c
[0, 1, 0], # 1
[1, 0, 0], # x
[0, 0, 0], # out
[0, 0, 0], # s1
[1, 0, 0], # y
[0, 0, 1] # s2
])
# out = s2 + 5
L4 = np.array([
# a b c
[5, 1, 0], # 1
[0, 0, 0], # x
[0, 0, 1], # out
[0, 0, 0], # s1
[0, 0, 0], # y
[1, 0, 0] # s2
])
a = np.array([L.transpose()[0] for L in (L1, L2, L3, L4)])
b = np.array([L.transpose()[1] for L in (L1, L2, L3, L4)])
c = np.array([L.transpose()[2] for L in (L1, L2, L3, L4)])
print("A")
print(a)
print("B")
print(b)
print("C")
print(c)
# The witness
s = np.array([
1,
3,
35,
9,
27,
30
])
print()
#print(s * a * s * b - s * c)
for a_i, b_i, c_i in zip(a, b, c):
assert sum(s * a_i) * sum(s * b_i) - sum(s * c_i) == 0
print("R1CS done.")
print()
def factorial(x):
r = 1
for x_i in range(2, x + 1):
r *= x_i
return r
def combinations(n, r):
return factorial(n) / (factorial(n - r) * factorial(r))
def lagrange(points):
result = np.poly1d([0])
for i, (x_i, y_i) in enumerate(points):
poly = np.poly1d([y_i])
for j, (x_j, y_j) in enumerate(points):
if i == j:
continue
poly *= np.poly1d([1, -x_j]) / (x_i - x_j)
#print(poly)
#print(poly(1), poly(2), poly(3))
result += poly
return result
# 1.5, -5.5, 7
#poly = lagrange([(1, 3), (2, 2), (3, 4)])
#print(poly)
def make_qap(a):
a_qap = []
a_polys = []
for a_i in a.transpose():
poly = lagrange(list(enumerate(a_i, start=1)))
coeffs = poly.c.tolist()
if len(coeffs) < 4:
coeffs = [0] * (4 - len(coeffs)) + coeffs
a_qap.append(coeffs)
a_polys.append(poly)
a_qap = np.array(a_qap)
print(a_qap)
return a_polys
print("A")
a_polys = make_qap(a)
print("B")
b_polys = make_qap(b)
print("C")
c_polys = make_qap(c)
def check(polys, x):
results = []
for poly in polys:
results.append(int(poly(x)))
return results
print()
print("A results at x", check(a_polys, 1))
print()
print("B results at x", check(b_polys, 1))
print()
print("C results at x", check(c_polys, 1))
def combine_polys(polys):
r = np.poly1d([0])
for s_i, p_i in zip(s, polys):
r += s_i * p_i
return r
print()
print()
A = combine_polys(a_polys)
print("A =")
print(A)
B = combine_polys(b_polys)
print("B =")
print(B)
C = combine_polys(c_polys)
print("C =")
print(C)
print()
t = A * B - C
print("t =")
print(t)
# 4 statements in our R1CS: L1, L2, L3, L4
divisor_poly = np.poly1d([1])
for x in range(1, 4 + 1):
divisor_poly *= np.poly1d([1, -x])
quot, remainder = np.polydiv(t, divisor_poly)
assert len(remainder.c) == 1
print()
print("Result of QAP:")
print(int(remainder.c[0]))