c-kzg-4844/bindings/python/py_ecc_tests.py

162 lines
4.2 KiB
Python
Raw Normal View History

2022-09-19 18:56:15 +00:00
import atexit
import ckzg
import kzg_proofs
def int_from_uint64s(digits):
"""Convert a 4-tuple of base64 digits to the int it denotes"""
res, mult = 0, 1
for x in digits:
res += mult * x
mult *= 2 ** 64
return res
def fr_from_int(x):
r = []
while x > 0:
r.append(x % 2**64)
x //= 2**64
assert len(r) <= 4
while len(r) < 4:
r.append(0)
return ckzg.fr_from_uint64s(tuple(r))
def fr_to_int(fr):
return int_from_uint64s(ckzg.fr_to_uint64s(fr))
import random
polynomial = [random.randint(0, 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001) for i in range(1024)]
n = len(polynomial)
secret_value = 1927409816240961209460912649124348975783457236447839525141982745761361378419
cfa = ckzg.frArray(len(polynomial))
for i, c in enumerate(polynomial):
cfa[i] = fr_from_int(c)
# Build the polynomial
ret, p = ckzg.new_poly_with_coeffs(cfa.cast(), len(polynomial))
assert ret == ckzg.C_KZG_OK
# Build a trusted setup with an arbitrary secret s
# and max scale 4 (so 16 secret values)
max_scale = n.bit_length() - 1
ret, fs = ckzg.new_fft_settings(max_scale)
assert ret == 0
sa = ckzg.byteArray(32)
for i, c in enumerate(secret_value.to_bytes(32, "little")):
sa[i] = c
ret, secret_s = ckzg.blst_scalar_from_le_bytes(sa.cast(), 32)
assert ret == 1
test_secret = ckzg.fr_from_scalar(secret_s)
assert fr_to_int(test_secret) == secret_value
num_secrets = 2 ** max_scale
g1s = ckzg.g1Array(num_secrets)
g2s = ckzg.g2Array(num_secrets)
ckzg.generate_trusted_setup(g1s.cast(), g2s.cast(), secret_s, num_secrets)
ret, ks = ckzg.new_kzg_settings(g1s.cast(), g2s.cast(), num_secrets, fs)
assert ret == 0
# Compute the Lagrange form of our polynomial in this setup
ret, p_l = ckzg.new_poly_l_from_poly(p, ks)
assert ret == 0
x = 832877253762587406983796272890571809375809175809315245
fr_x = fr_from_int(x)
assert fr_to_int(fr_x) == x
ret, y_l = ckzg.eval_poly_l(p_l, fr_x, fs)
assert ret == 0
y = ckzg.eval_poly(p, fr_x)
assert ckzg.fr_equal(y, y_l)
y_p = kzg_proofs.eval_poly_at(polynomial, x)
assert fr_to_int(y) == y_p
# Commit to the polynomial, in both Lagrange and coefficient form
# The commitment should be the same
ret, commitment = ckzg.commit_to_poly(p, ks)
assert ret == 0
ret, commitment_l = ckzg.commit_to_poly_l(p_l, ks)
assert ret == 0
assert ckzg.g1_equal(commitment, commitment_l)
# Compute proof at an arbitrary point (for both forms)
ret, π = ckzg.compute_proof_single(p, fr_x, ks)
assert ret == 0
ret, v = ckzg.eval_poly_l(p_l, fr_x, fs)
assert ret == 0
ret, π_l = ckzg.compute_proof_single_l(p_l, fr_x, v, ks)
assert ret == 0
# Compute proof using py_ecc
pyecc_setup = kzg_proofs.generate_setup(secret_value, n)
pyecc_commitment = kzg_proofs.commit_to_poly(polynomial, pyecc_setup)
pyecc_proof = kzg_proofs.compute_proof_single(polynomial, x, pyecc_setup)
assert kzg_proofs.check_proof_single(pyecc_commitment, pyecc_proof, x, y_p, pyecc_setup)
from py_ecc.bls.point_compression import compress_G1
pyecc_commitment_compressed = compress_G1(pyecc_commitment)
pyecc_proof_compressed = compress_G1(pyecc_proof)
commitment_compressed = ckzg.byteArray(48)
ckzg.blst_p1_compress(commitment_compressed.cast(), commitment)
commitment_compressed = bytes([commitment_compressed[i] for i in range(48)])
assert commitment_compressed == pyecc_commitment_compressed.to_bytes(48, "big")
proof_compressed = ckzg.byteArray(48)
ckzg.blst_p1_compress(proof_compressed.cast(), π)
proof_compressed = bytes([proof_compressed[i] for i in range(48)])
assert proof_compressed == pyecc_proof_compressed.to_bytes(48, "big")
# Check the proofs using the commitments
ret, res = ckzg.check_proof_single(commitment, π, fr_x, v, ks)
assert ret == 0
assert res
ret, res = ckzg.check_proof_single(commitment_l, π_l, fr_x, v, ks)
assert ret == 0
assert res
# Check the proof fails with the wrong value
w = ckzg.fr_add(v, ckzg.fr_one)
ret, res = ckzg.check_proof_single(commitment_l, π_l, fr_x, w, ks)
assert ret == 0
assert not res
print("All tests passed.")
# We need to manually free the C allocated arrays
# Use atexit so this file can be loaded interactively before freeing
def cleanup():
ckzg.free_poly(p)
ckzg.free_poly_l(p_l)
ckzg.free_fft_settings(fs)
ckzg.free_kzg_settings(ks)
atexit.register(cleanup)