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]))