You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This document specifies basic polynomial operations and KZG polynomial commitment operations as they are needed for the EIP-4844 specification. The implementations are not optimized for performance, but readability. All practical implementations should optimize the polynomial operations.
Custom types
Name
SSZ equivalent
Description
G1Point
Bytes48
G2Point
Bytes96
BLSFieldElement
uint256
x < BLS_MODULUS
KZGCommitment
Bytes48
Same as BLS standard "is valid pubkey" check but also allows 0x00..00 for point-at-infinity
Roots of unity of order FIELD_ELEMENTS_PER_BLOB over the BLS12-381 field
Trusted setup
The trusted setup is part of the preset: during testing a minimal insecure variant may be used,
but reusing the mainnet settings in public networks is a critical security requirement.
All polynomials (which are always given in Lagrange form) should be interpreted as being in
bit-reversal permutation. In practice, clients can implement this by storing the lists
KZG_SETUP_LAGRANGE and ROOTS_OF_UNITY in bit-reversal permutation, so these functions only
have to be called once at startup.
is_power_of_two
defis_power_of_two(value: int) ->bool:
""" Check if ``value`` is a power of two integer. """return (value>0) and (value& (value-1) ==0)
reverse_bits
defreverse_bits(n: int, order: int) ->int:
""" Reverse the bit order of an integer ``n``. """assertis_power_of_two(order)
# Convert n to binary with the same number of bits as "order" - 1, then reverse its bit orderreturnint(('{:0'+str(order.bit_length() -1) +'b}').format(n)[::-1], 2)
bit_reversal_permutation
defbit_reversal_permutation(sequence: Sequence[T]) ->Sequence[T]:
""" Return a copy with bit-reversed permutation. The permutation is an involution (inverts itself). The input and output are a sequence of generic type ``T`` objects. """return [sequence[reverse_bits(i, len(sequence))] foriinrange(len(sequence))]
BLS12-381 helpers
bytes_to_bls_field
defbytes_to_bls_field(b: Bytes32) ->BLSFieldElement:
""" Convert 32-byte value to a BLS field scalar. The output is not uniform over the BLS field. """returnint.from_bytes(b, ENDIANNESS) %BLS_MODULUS
blob_to_polynomial
defblob_to_polynomial(blob: Blob) ->Polynomial:
""" Convert a blob to list of BLS field scalars. """polynomial=Polynomial()
foriinrange(FIELD_ELEMENTS_PER_BLOB):
value=int.from_bytes(blob[i*BYTES_PER_FIELD_ELEMENT: (i+1) *BYTES_PER_FIELD_ELEMENT], ENDIANNESS)
assertvalue<BLS_MODULUSpolynomial[i] =valuereturnpolynomial
hash_to_bls_field
defhash_to_bls_field(polys: Sequence[Polynomial],
comms: Sequence[KZGCommitment]) ->BLSFieldElement:
""" Compute 32-byte hash of serialized polynomials and commitments concatenated. This hash is then converted to a BLS field element, where the result is not uniform over the BLS field. Return the BLS field element. """# Append the number of polynomials and the degree of each polynomial as a domain separatornum_polys=int.to_bytes(len(polys), 8, ENDIANNESS)
degree_poly=int.to_bytes(FIELD_ELEMENTS_PER_BLOB, 8, ENDIANNESS)
data=FIAT_SHAMIR_PROTOCOL_DOMAIN+degree_poly+num_polys# Append each polynomial which is composed by field elementsforpolyinpolys:
forfield_elementinpoly:
data+=int.to_bytes(field_element, BYTES_PER_FIELD_ELEMENT, ENDIANNESS)
# Append serialized G1 pointsforcommitmentincomms:
data+=commitmentreturnbytes_to_bls_field(hash(data))
bls_modular_inverse
defbls_modular_inverse(x: BLSFieldElement) ->BLSFieldElement:
""" Compute the modular inverse of x i.e. return y such that x * y % BLS_MODULUS == 1 and return 0 for x == 0 """returnpow(x, -1, BLS_MODULUS) ifx!=0else0
div
defdiv(x: BLSFieldElement, y: BLSFieldElement) ->BLSFieldElement:
""" Divide two field elements: ``x`` by `y``. """return (int(x) *int(bls_modular_inverse(y))) %BLS_MODULUS
g1_lincomb
defg1_lincomb(points: Sequence[KZGCommitment], scalars: Sequence[BLSFieldElement]) ->KZGCommitment:
""" BLS multiscalar multiplication. This function can be optimized using Pippenger's algorithm and variants. """assertlen(points) ==len(scalars)
result=bls.Z1forx, ainzip(points, scalars):
result=bls.add(result, bls.multiply(bls.bytes48_to_G1(x), a))
returnKZGCommitment(bls.G1_to_bytes48(result))
poly_lincomb
defpoly_lincomb(polys: Sequence[Polynomial],
scalars: Sequence[BLSFieldElement]) ->Polynomial:
""" Given a list of ``polynomials``, interpret it as a 2D matrix and compute the linear combination of each column with `scalars`: return the resulting polynomials. """result= [0] *len(polys[0])
forv, sinzip(polys, scalars):
fori, xinenumerate(v):
result[i] = (result[i] +int(s) *int(x)) %BLS_MODULUSreturn [BLSFieldElement(x) forxinresult]
compute_powers
defcompute_powers(x: BLSFieldElement, n: uint64) ->Sequence[BLSFieldElement]:
""" Return ``x`` to power of [0, n-1]. """current_power=1powers= []
for_inrange(n):
powers.append(BLSFieldElement(current_power))
current_power=current_power*int(x) %BLS_MODULUSreturnpowers
Polynomials
evaluate_polynomial_in_evaluation_form
defevaluate_polynomial_in_evaluation_form(polynomial: Polynomial,
z: BLSFieldElement) ->BLSFieldElement:
""" Evaluate a polynomial (in evaluation form) at an arbitrary point ``z``. Uses the barycentric formula: f(z) = (z**WIDTH - 1) / WIDTH * sum_(i=0)^WIDTH (f(DOMAIN[i]) * DOMAIN[i]) / (z - DOMAIN[i]) """width=len(polynomial)
assertwidth==FIELD_ELEMENTS_PER_BLOBinverse_width=bls_modular_inverse(width)
# Make sure we won't divide by zero during divisionassertznotinROOTS_OF_UNITYroots_of_unity_brp=bit_reversal_permutation(ROOTS_OF_UNITY)
result=0foriinrange(width):
result+=div(int(polynomial[i]) *int(roots_of_unity_brp[i]), (int(z) -roots_of_unity_brp[i]))
result=result* (pow(z, width, BLS_MODULUS) -1) *inverse_width%BLS_MODULUSreturnresult
KZG
KZG core functions. These are also defined in EIP-4844 execution specs.
defverify_kzg_proof(polynomial_kzg: KZGCommitment,
z: BLSFieldElement,
y: BLSFieldElement,
kzg_proof: KZGProof) ->bool:
""" Verify KZG proof that ``p(z) == y`` where ``p(z)`` is the polynomial represented by ``polynomial_kzg``. """# Verify: P - y = Q * (X - z)X_minus_z=bls.add(bls.bytes96_to_G2(KZG_SETUP_G2[1]), bls.multiply(bls.G2, BLS_MODULUS-z))
P_minus_y=bls.add(bls.bytes48_to_G1(polynomial_kzg), bls.multiply(bls.G1, BLS_MODULUS-y))
returnbls.pairing_check([
[P_minus_y, bls.neg(bls.G2)],
[bls.bytes48_to_G1(kzg_proof), X_minus_z]
])
compute_kzg_proof
defcompute_kzg_proof(polynomial: Polynomial, z: BLSFieldElement) ->KZGProof:
""" Compute KZG proof at point `z` with `polynomial` being in evaluation form Do this by computing the quotient polynomial in evaluation form: q(x) = (p(x) - p(z)) / (x - z) """# To avoid SSZ overflow/underflow, convert element into intpolynomial= [int(i) foriinpolynomial]
z=int(z)
y=evaluate_polynomial_in_evaluation_form(polynomial, z)
polynomial_shifted= [(p-int(y)) %BLS_MODULUSforpinpolynomial]
# Make sure we won't divide by zero during divisionassertznotinROOTS_OF_UNITYdenominator_poly= [(x-z) %BLS_MODULUSforxinbit_reversal_permutation(ROOTS_OF_UNITY)]
# Calculate quotient polynomial by doing point-by-point divisionquotient_polynomial= [div(a, b) fora, binzip(polynomial_shifted, denominator_poly)]
returnKZGProof(g1_lincomb(bit_reversal_permutation(KZG_SETUP_LAGRANGE), quotient_polynomial))
compute_aggregated_poly_and_commitment
defcompute_aggregated_poly_and_commitment(
blobs: Sequence[Blob],
kzg_commitments: Sequence[KZGCommitment]) ->Tuple[Polynomial, KZGCommitment, BLSFieldElement]:
""" Return (1) the aggregated polynomial, (2) the aggregated KZG commitment, and (3) the polynomial evaluation random challenge. """# Generate random linear combination challengesr=hash_to_bls_field(blobs, kzg_commitments)
r_powers=compute_powers(r, len(kzg_commitments))
evaluation_challenge=int(r_powers[-1]) *r%BLS_MODULUS# Create aggregated polynomial in evaluation formaggregated_poly=Polynomial(poly_lincomb([blob_to_polynomial(blob) forblobinblobs], r_powers))
# Compute commitment to aggregated polynomialaggregated_poly_commitment=KZGCommitment(g1_lincomb(kzg_commitments, r_powers))
returnaggregated_poly, aggregated_poly_commitment, evaluation_challenge