def fk20_single_data_availability_optimized(polynomial, setup): """ Special version of the FK20 for the situation of data availability checks: The upper half of the polynomial coefficients is always 0, so we do not need to extend to twice the size for Toeplitz matrix multiplication """ assert is_power_of_two(len(polynomial)) n = len(polynomial) // 2 assert all(x == 0 for x in polynomial[n:]) reduced_polynomial = polynomial[:n] # Preprocessing part -- this is independent from the polynomial coefficients and can be # done before the polynomial is known, it only needs to be computed once x = setup[0][n - 2::-1] + [b.Z1] xext_fft = toeplitz_part1(x) toeplitz_coefficients = reduced_polynomial[-1::] + [0] * ( n + 1) + reduced_polynomial[1:-1] # Compute the vector h from the paper using a Toeplitz matric multiplication h = toeplitz_part3(toeplitz_part2(toeplitz_coefficients, xext_fft)) h = h + [b.Z1] * n # The proofs are the DFT of the h vector return fft(h, MODULUS, get_root_of_unity(2 * n))
def toeplitz_part1(x): """ Performs the first part of the Toeplitz matrix multiplication algorithm, which is a Fourier transform of the vector x extended """ assert is_power_of_two(len(x)) root_of_unity = get_root_of_unity(len(x) * 2) # Extend x with zeros (neutral element of G1) xext = x + [b.Z1] * len(x) xext_fft = fft(xext, MODULUS, root_of_unity, inv=False) return xext_fft
def fk20_single(polynomial, setup): """ Compute all n (single) proofs according to FK20 method """ assert is_power_of_two(len(polynomial)) n = len(polynomial) x = setup[0][n - 2::-1] + [b.Z1] xext_fft = toeplitz_part1(x) toeplitz_coefficients = polynomial[-1::] + [0] * (n + 1) + polynomial[1:-1] # Compute the vector h from the paper using a Toeplitz matric multiplication h = toeplitz_part3(toeplitz_part2(toeplitz_coefficients, xext_fft)) # The proofs are the DFT of the h vector return fft(h, MODULUS, get_root_of_unity(n))
def fk20_multi_data_availability_optimized(polynomial, l, setup): """ FK20 multi-proof method, optimized for dava availability where the top half of polynomial coefficients == 0 """ assert is_power_of_two(len(polynomial)) n = len(polynomial) // 2 k = n // l assert is_power_of_two(n) assert is_power_of_two(l) assert k >= 1 assert all(x == 0 for x in polynomial[n:]) reduced_polynomial = polynomial[:n] # Preprocessing part -- this is independent from the polynomial coefficients and can be # done before the polynomial is known, it only needs to be computed once xext_fft = [] for i in range(l): x = setup[0][n - l - 1 - i::-l] + [b.Z1] xext_fft.append(toeplitz_part1(x)) add_instrumentation() hext_fft = [b.Z1] * 2 * k for i in range(l): toeplitz_coefficients = reduced_polynomial[- i - 1::l] + [0] * (k + 1) \ + reduced_polynomial[2 * l - i - 1: - l - i:l] # Compute the vector h from the paper using a Toeplitz matric multiplication hext_fft = [ b.add(v, w) for v, w in zip( hext_fft, toeplitz_part2(toeplitz_coefficients, xext_fft[i])) ] # Final FFT done after summing all h vectors h = toeplitz_part3(hext_fft) h = h + [b.Z1] * k # The proofs are the DFT of the h vector return fft(h, MODULUS, get_root_of_unity(2 * k))
def toeplitz_part2(toeplitz_coefficients, xext_fft): """ Performs the second part of the Toeplitz matrix multiplication algorithm """ # Extend the toeplitz coefficients to get a circulant matrix into which the Toeplitz # matrix is embedded assert is_power_of_two(len(toeplitz_coefficients)) root_of_unity = get_root_of_unity(len(xext_fft)) toeplitz_coefficients_fft = fft(toeplitz_coefficients, MODULUS, root_of_unity, inv=False) hext_fft = [ b.multiply(v, w) for v, w in zip(xext_fft, toeplitz_coefficients_fft) ] return hext_fft
def fk20_multi(polynomial, l, setup): """ For a polynomial of size n, let w be a n-th root of unity. Then this method will return k=n/l KZG proofs for the points proof[0]: w^(0*l + 0), w^(0*l + 1), ... w^(0*l + l - 1) proof[0]: w^(0*l + 0), w^(0*l + 1), ... w^(0*l + l - 1) ... proof[i]: w^(i*l + 0), w^(i*l + 1), ... w^(i*l + l - 1) ... """ n = len(polynomial) k = n // l assert is_power_of_two(n) assert is_power_of_two(l) assert k >= 1 # Preprocessing part -- this is independent from the polynomial coefficients and can be # done before the polynomial is known, it only needs to be computed once xext_fft = [] for i in range(l): x = setup[0][n - l - 1 - i::-l] + [b.Z1] xext_fft.append(toeplitz_part1(x)) hext_fft = [b.Z1] * 2 * k for i in range(l): toeplitz_coefficients = polynomial[-i - 1::l] + [0] * ( k + 1) + polynomial[2 * l - i - 1:-l - i:l] # Compute the vector h from the paper using a Toeplitz matric multiplication hext_fft = [ b.add(v, w) for v, w in zip( hext_fft, toeplitz_part2(toeplitz_coefficients, xext_fft[i])) ] h = toeplitz_part3(hext_fft) # The proofs are the DFT of the h vector return fft(h, MODULUS, get_root_of_unity(k))
def toeplitz_part3(hext_fft): root_of_unity = get_root_of_unity(len(hext_fft)) # Transform back and return the first half of the vector # Only the top half is the Toeplitz product, the rest is padding return fft(hext_fft, MODULUS, root_of_unity, inv=True)[:len(hext_fft) // 2]
extended_polynomial = polynomial + [0] * n all_proofs = fk20_single_data_availability_optimized( extended_polynomial, setup) return list_to_reverse_bit_order(all_proofs) if __name__ == "__main__": polynomial = [1, 2, 3, 4, 7, 7, 7, 7, 13, 13, 13, 13, 13, 13, 13, 13] n = len(polynomial) setup = generate_setup(1927409816240961209460912649124, n) commitment = commit_to_poly(polynomial, setup) # Computing the proofs on the double all_proofs = data_availabilty_using_fk20(polynomial, setup) print("All KZG proofs computed") # Now check a random position pos = 9 root_of_unity = get_root_of_unity(n * 2) x = pow(root_of_unity, pos, MODULUS) y = eval_poly_at(polynomial, x) assert check_proof_single(commitment, all_proofs[reverse_bit_order(pos, 2 * n)], x, y, setup) print("Single point check passed")
setup = generate_setup(1927409816240961209460912649124, n) commitment = commit_to_poly(polynomial, setup) l = 16 all_proofs = data_availabilty_using_fk20_multi(polynomial, l, setup) print( "All KZG proofs computed for data availability (supersampled by factor 2)" ) print("Required {0} G1 multiplications".format(multiplication_count)) print(n, l, multiplication_count) # Now check all positions extended_data = get_extended_data(polynomial) for pos in range(2 * n // l): root_of_unity = get_root_of_unity(n * 2) x = pow(root_of_unity, reverse_bit_order(pos, 2 * n // l), MODULUS) ys = extended_data[l * pos:l * (pos + 1)] subgroup_root_of_unity = get_root_of_unity(l) coset = [x * pow(subgroup_root_of_unity, i, MODULUS) for i in range(l)] ys2 = [eval_poly_at(polynomial, z) for z in coset] assert list_to_reverse_bit_order(ys) == ys2 assert check_proof_multi(commitment, all_proofs[pos], x, list_to_reverse_bit_order(ys), setup) print("Data availability sample check {0} passed".format(pos))