162 lines
4.2 KiB
Python
162 lines
4.2 KiB
Python
|
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)
|