Test generators for kzg-4844 libraries (#3274)
Arkworks integration and test generators for kzg-4844 libraries
This commit is contained in:
parent
9d01722904
commit
86fb82b221
4
Makefile
4
Makefile
|
@ -117,10 +117,10 @@ citest: pyspec
|
|||
mkdir -p $(TEST_REPORT_DIR);
|
||||
ifdef fork
|
||||
. venv/bin/activate; cd $(PY_SPEC_DIR); \
|
||||
python3 -m pytest -n 16 --bls-type=milagro --preset=$(TEST_PRESET_TYPE) --fork=$(fork) --junitxml=test-reports/test_results.xml eth2spec
|
||||
python3 -m pytest -n 16 --bls-type=fastest --preset=$(TEST_PRESET_TYPE) --fork=$(fork) --junitxml=test-reports/test_results.xml eth2spec
|
||||
else
|
||||
. venv/bin/activate; cd $(PY_SPEC_DIR); \
|
||||
python3 -m pytest -n 16 --bls-type=milagro --preset=$(TEST_PRESET_TYPE) --junitxml=test-reports/test_results.xml eth2spec
|
||||
python3 -m pytest -n 16 --bls-type=fastest --preset=$(TEST_PRESET_TYPE) --junitxml=test-reports/test_results.xml eth2spec
|
||||
endif
|
||||
|
||||
|
||||
|
|
1
setup.py
1
setup.py
|
@ -1174,5 +1174,6 @@ setup(
|
|||
RUAMEL_YAML_VERSION,
|
||||
"lru-dict==1.1.8",
|
||||
MARKO_VERSION,
|
||||
"py_arkworks_bls12381==0.3.4",
|
||||
]
|
||||
)
|
||||
|
|
|
@ -273,7 +273,7 @@ def g1_lincomb(points: Sequence[KZGCommitment], scalars: Sequence[BLSFieldElemen
|
|||
BLS multiscalar multiplication. This function can be optimized using Pippenger's algorithm and variants.
|
||||
"""
|
||||
assert len(points) == len(scalars)
|
||||
result = bls.Z1
|
||||
result = bls.Z1()
|
||||
for x, a in zip(points, scalars):
|
||||
result = bls.add(result, bls.multiply(bls.bytes48_to_G1(x), a))
|
||||
return KZGCommitment(bls.G1_to_bytes48(result))
|
||||
|
@ -323,7 +323,7 @@ def evaluate_polynomial_in_evaluation_form(polynomial: Polynomial,
|
|||
a = BLSFieldElement(int(polynomial[i]) * int(roots_of_unity_brp[i]) % BLS_MODULUS)
|
||||
b = BLSFieldElement((int(BLS_MODULUS) + int(z) - int(roots_of_unity_brp[i])) % BLS_MODULUS)
|
||||
result += int(div(a, b) % BLS_MODULUS)
|
||||
result = result * int(pow(z, width, BLS_MODULUS) - 1) * int(inverse_width)
|
||||
result = result * int(BLS_MODULUS + pow(z, width, BLS_MODULUS) - 1) * int(inverse_width)
|
||||
return BLSFieldElement(result % BLS_MODULUS)
|
||||
```
|
||||
|
||||
|
@ -371,10 +371,10 @@ def verify_kzg_proof_impl(commitment: KZGCommitment,
|
|||
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(commitment), bls.multiply(bls.G1, BLS_MODULUS - y))
|
||||
X_minus_z = bls.add(bls.bytes96_to_G2(KZG_SETUP_G2[1]), bls.multiply(bls.G2(), (BLS_MODULUS - z) % BLS_MODULUS))
|
||||
P_minus_y = bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1(), (BLS_MODULUS - y) % BLS_MODULUS))
|
||||
return bls.pairing_check([
|
||||
[P_minus_y, bls.neg(bls.G2)],
|
||||
[P_minus_y, bls.neg(bls.G2())],
|
||||
[bls.bytes48_to_G1(proof), X_minus_z]
|
||||
])
|
||||
```
|
||||
|
@ -415,14 +415,14 @@ def verify_kzg_proof_batch(commitments: Sequence[KZGCommitment],
|
|||
proofs,
|
||||
[BLSFieldElement((int(z) * int(r_power)) % BLS_MODULUS) for z, r_power in zip(zs, r_powers)],
|
||||
)
|
||||
C_minus_ys = [bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1, BLS_MODULUS - y))
|
||||
C_minus_ys = [bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1(), (BLS_MODULUS - y) % BLS_MODULUS))
|
||||
for commitment, y in zip(commitments, ys)]
|
||||
C_minus_y_as_KZGCommitments = [KZGCommitment(bls.G1_to_bytes48(x)) for x in C_minus_ys]
|
||||
C_minus_y_lincomb = g1_lincomb(C_minus_y_as_KZGCommitments, r_powers)
|
||||
|
||||
return bls.pairing_check([
|
||||
[bls.bytes48_to_G1(proof_lincomb), bls.neg(bls.bytes96_to_G2(KZG_SETUP_G2[1]))],
|
||||
[bls.add(bls.bytes48_to_G1(C_minus_y_lincomb), bls.bytes48_to_G1(proof_z_lincomb)), bls.G2]
|
||||
[bls.add(bls.bytes48_to_G1(C_minus_y_lincomb), bls.bytes48_to_G1(proof_z_lincomb)), bls.G2()]
|
||||
])
|
||||
```
|
||||
|
||||
|
@ -561,3 +561,4 @@ def verify_blob_kzg_proof_batch(blobs: Sequence[Blob],
|
|||
|
||||
return verify_kzg_proof_batch(commitments, evaluation_challenges, ys, proofs)
|
||||
```
|
||||
|
||||
|
|
|
@ -44,8 +44,11 @@ def pytest_addoption(parser):
|
|||
help="bls-default: make tests that are not dependent on BLS run without BLS"
|
||||
)
|
||||
parser.addoption(
|
||||
"--bls-type", action="store", type=str, default="py_ecc", choices=["py_ecc", "milagro"],
|
||||
help="bls-type: use 'pyecc' or 'milagro' implementation for BLS"
|
||||
"--bls-type", action="store", type=str, default="py_ecc", choices=["py_ecc", "milagro", "arkworks", "fastest"],
|
||||
help=(
|
||||
"bls-type: use specified BLS implementation;"
|
||||
"fastest: use milagro for signatures and arkworks for everything else (e.g. KZG)"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
@ -88,5 +91,9 @@ def bls_type(request):
|
|||
bls_utils.use_py_ecc()
|
||||
elif bls_type == "milagro":
|
||||
bls_utils.use_milagro()
|
||||
elif bls_type == "arkworks":
|
||||
bls_utils.use_arkworks()
|
||||
elif bls_type == "fastest":
|
||||
bls_utils.use_fastest()
|
||||
else:
|
||||
raise Exception(f"unrecognized bls type: {bls_type}")
|
||||
|
|
|
@ -1,28 +1,49 @@
|
|||
from py_ecc.bls import G2ProofOfPossession as py_ecc_bls
|
||||
from py_ecc.bls.g2_primatives import signature_to_G2 as _signature_to_G2
|
||||
from py_ecc.optimized_bls12_381 import ( # noqa: F401
|
||||
G1,
|
||||
G2,
|
||||
Z1,
|
||||
Z2,
|
||||
FQ,
|
||||
add,
|
||||
multiply,
|
||||
neg,
|
||||
pairing,
|
||||
final_exponentiate,
|
||||
FQ12
|
||||
G1 as py_ecc_G1,
|
||||
G2 as py_ecc_G2,
|
||||
Z1 as py_ecc_Z1,
|
||||
add as py_ecc_add,
|
||||
multiply as py_ecc_mul,
|
||||
neg as py_ecc_neg,
|
||||
pairing as py_ecc_pairing,
|
||||
final_exponentiate as py_ecc_final_exponentiate,
|
||||
FQ12 as py_ecc_GT,
|
||||
)
|
||||
from py_ecc.bls.g2_primitives import ( # noqa: F401
|
||||
G1_to_pubkey as G1_to_bytes48,
|
||||
pubkey_to_G1 as bytes48_to_G1,
|
||||
G2_to_signature as G2_to_bytes96,
|
||||
signature_to_G2 as bytes96_to_G2,
|
||||
G1_to_pubkey as py_ecc_G1_to_bytes48,
|
||||
pubkey_to_G1 as py_ecc_bytes48_to_G1,
|
||||
G2_to_signature as py_ecc_G2_to_bytes96,
|
||||
signature_to_G2 as py_ecc_bytes96_to_G2,
|
||||
)
|
||||
from py_arkworks_bls12381 import (
|
||||
G1Point as arkworks_G1,
|
||||
G2Point as arkworks_G2,
|
||||
Scalar as arkworks_Scalar,
|
||||
GT as arkworks_GT,
|
||||
)
|
||||
|
||||
|
||||
import milagro_bls_binding as milagro_bls # noqa: F401 for BLS switching option
|
||||
|
||||
import py_arkworks_bls12381 as arkworks_bls # noqa: F401 for BLS switching option
|
||||
|
||||
|
||||
class fastest_bls:
|
||||
G1 = arkworks_G1
|
||||
G2 = arkworks_G2
|
||||
Scalar = arkworks_Scalar
|
||||
GT = arkworks_GT
|
||||
_AggregatePKs = milagro_bls._AggregatePKs
|
||||
Sign = milagro_bls.Sign
|
||||
Verify = milagro_bls.Verify
|
||||
Aggregate = milagro_bls.Aggregate
|
||||
AggregateVerify = milagro_bls.AggregateVerify
|
||||
FastAggregateVerify = milagro_bls.FastAggregateVerify
|
||||
SkToPk = milagro_bls.SkToPk
|
||||
|
||||
|
||||
# Flag to make BLS active or not. Used for testing, do not ignore BLS in production unless you know what you are doing.
|
||||
bls_active = True
|
||||
|
||||
|
@ -43,6 +64,14 @@ def use_milagro():
|
|||
bls = milagro_bls
|
||||
|
||||
|
||||
def use_arkworks():
|
||||
"""
|
||||
Shortcut to use Arkworks as BLS library
|
||||
"""
|
||||
global bls
|
||||
bls = arkworks_bls
|
||||
|
||||
|
||||
def use_py_ecc():
|
||||
"""
|
||||
Shortcut to use Py-ecc as BLS library
|
||||
|
@ -51,6 +80,14 @@ def use_py_ecc():
|
|||
bls = py_ecc_bls
|
||||
|
||||
|
||||
def use_fastest():
|
||||
"""
|
||||
Shortcut to use Milagro for signatures and Arkworks for other BLS operations
|
||||
"""
|
||||
global bls
|
||||
bls = fastest_bls
|
||||
|
||||
|
||||
def only_with_bls(alt_return=None):
|
||||
"""
|
||||
Decorator factory to make a function only run when BLS is active. Otherwise return the default.
|
||||
|
@ -68,7 +105,10 @@ def only_with_bls(alt_return=None):
|
|||
@only_with_bls(alt_return=True)
|
||||
def Verify(PK, message, signature):
|
||||
try:
|
||||
result = bls.Verify(PK, message, signature)
|
||||
if bls == arkworks_bls: # no signature API in arkworks
|
||||
result = py_ecc_bls.Verify(PK, message, signature)
|
||||
else:
|
||||
result = bls.Verify(PK, message, signature)
|
||||
except Exception:
|
||||
result = False
|
||||
finally:
|
||||
|
@ -78,7 +118,10 @@ def Verify(PK, message, signature):
|
|||
@only_with_bls(alt_return=True)
|
||||
def AggregateVerify(pubkeys, messages, signature):
|
||||
try:
|
||||
result = bls.AggregateVerify(list(pubkeys), list(messages), signature)
|
||||
if bls == arkworks_bls: # no signature API in arkworks
|
||||
result = py_ecc_bls.AggregateVerify(list(pubkeys), list(messages), signature)
|
||||
else:
|
||||
result = bls.AggregateVerify(list(pubkeys), list(messages), signature)
|
||||
except Exception:
|
||||
result = False
|
||||
finally:
|
||||
|
@ -88,7 +131,10 @@ def AggregateVerify(pubkeys, messages, signature):
|
|||
@only_with_bls(alt_return=True)
|
||||
def FastAggregateVerify(pubkeys, message, signature):
|
||||
try:
|
||||
result = bls.FastAggregateVerify(list(pubkeys), message, signature)
|
||||
if bls == arkworks_bls: # no signature API in arkworks
|
||||
result = py_ecc_bls.FastAggregateVerify(list(pubkeys), message, signature)
|
||||
else:
|
||||
result = bls.FastAggregateVerify(list(pubkeys), message, signature)
|
||||
except Exception:
|
||||
result = False
|
||||
finally:
|
||||
|
@ -97,12 +143,16 @@ def FastAggregateVerify(pubkeys, message, signature):
|
|||
|
||||
@only_with_bls(alt_return=STUB_SIGNATURE)
|
||||
def Aggregate(signatures):
|
||||
if bls == arkworks_bls: # no signature API in arkworks
|
||||
return py_ecc_bls.Aggregate(signatures)
|
||||
return bls.Aggregate(signatures)
|
||||
|
||||
|
||||
@only_with_bls(alt_return=STUB_SIGNATURE)
|
||||
def Sign(SK, message):
|
||||
if bls == py_ecc_bls:
|
||||
if bls == arkworks_bls: # no signature API in arkworks
|
||||
return py_ecc_bls.Sign(SK, message)
|
||||
elif bls == py_ecc_bls:
|
||||
return bls.Sign(SK, message)
|
||||
else:
|
||||
return bls.Sign(SK.to_bytes(32, 'big'), message)
|
||||
|
@ -121,24 +171,143 @@ def AggregatePKs(pubkeys):
|
|||
# milagro_bls._AggregatePKs checks KeyValidate internally
|
||||
pass
|
||||
|
||||
if bls == arkworks_bls: # no signature API in arkworks
|
||||
return py_ecc_bls._AggregatePKs(list(pubkeys))
|
||||
|
||||
return bls._AggregatePKs(list(pubkeys))
|
||||
|
||||
|
||||
@only_with_bls(alt_return=STUB_SIGNATURE)
|
||||
def SkToPk(SK):
|
||||
if bls == py_ecc_bls:
|
||||
return bls.SkToPk(SK)
|
||||
if bls == py_ecc_bls or bls == arkworks_bls: # no signature API in arkworks
|
||||
return py_ecc_bls.SkToPk(SK)
|
||||
else:
|
||||
return bls.SkToPk(SK.to_bytes(32, 'big'))
|
||||
|
||||
|
||||
def pairing_check(values):
|
||||
p_q_1, p_q_2 = values
|
||||
final_exponentiation = final_exponentiate(
|
||||
pairing(p_q_1[1], p_q_1[0], final_exponentiate=False)
|
||||
* pairing(p_q_2[1], p_q_2[0], final_exponentiate=False)
|
||||
)
|
||||
return final_exponentiation == FQ12.one()
|
||||
if bls == arkworks_bls or bls == fastest_bls:
|
||||
p_q_1, p_q_2 = values
|
||||
g1s = [p_q_1[0], p_q_2[0]]
|
||||
g2s = [p_q_1[1], p_q_2[1]]
|
||||
return arkworks_GT.multi_pairing(g1s, g2s) == arkworks_GT.one()
|
||||
else:
|
||||
p_q_1, p_q_2 = values
|
||||
final_exponentiation = py_ecc_final_exponentiate(
|
||||
py_ecc_pairing(p_q_1[1], p_q_1[0], final_exponentiate=False)
|
||||
* py_ecc_pairing(p_q_2[1], p_q_2[0], final_exponentiate=False)
|
||||
)
|
||||
return final_exponentiation == py_ecc_GT.one()
|
||||
|
||||
|
||||
def add(lhs, rhs):
|
||||
"""
|
||||
Performs point addition of `lhs` and `rhs`.
|
||||
The points can either be in G1 or G2.
|
||||
"""
|
||||
if bls == arkworks_bls or bls == fastest_bls:
|
||||
return lhs + rhs
|
||||
return py_ecc_add(lhs, rhs)
|
||||
|
||||
|
||||
def multiply(point, scalar):
|
||||
"""
|
||||
Performs Scalar multiplication between
|
||||
`point` and `scalar`.
|
||||
`point` can either be in G1 or G2
|
||||
"""
|
||||
if bls == arkworks_bls or bls == fastest_bls:
|
||||
int_as_bytes = scalar.to_bytes(32, 'little')
|
||||
scalar = arkworks_Scalar.from_le_bytes(int_as_bytes)
|
||||
return point * scalar
|
||||
return py_ecc_mul(point, scalar)
|
||||
|
||||
|
||||
def neg(point):
|
||||
"""
|
||||
Returns the point negation of `point`
|
||||
`point` can either be in G1 or G2
|
||||
"""
|
||||
if bls == arkworks_bls or bls == fastest_bls:
|
||||
return -point
|
||||
return py_ecc_neg(point)
|
||||
|
||||
|
||||
def Z1():
|
||||
"""
|
||||
Returns the identity point in G1
|
||||
"""
|
||||
if bls == arkworks_bls or bls == fastest_bls:
|
||||
return arkworks_G1.identity()
|
||||
return py_ecc_Z1
|
||||
|
||||
|
||||
def G1():
|
||||
"""
|
||||
Returns the chosen generator point in G1
|
||||
"""
|
||||
if bls == arkworks_bls or bls == fastest_bls:
|
||||
return arkworks_G1()
|
||||
return py_ecc_G1
|
||||
|
||||
|
||||
def G2():
|
||||
"""
|
||||
Returns the chosen generator point in G2
|
||||
"""
|
||||
if bls == arkworks_bls or bls == fastest_bls:
|
||||
return arkworks_G2()
|
||||
return py_ecc_G2
|
||||
|
||||
|
||||
def G1_to_bytes48(point):
|
||||
"""
|
||||
Serializes a point in G1.
|
||||
Returns a bytearray of size 48 as
|
||||
we use the compressed format
|
||||
"""
|
||||
if bls == arkworks_bls or bls == fastest_bls:
|
||||
return bytes(point.to_compressed_bytes())
|
||||
return py_ecc_G1_to_bytes48(point)
|
||||
|
||||
|
||||
def G2_to_bytes96(point):
|
||||
"""
|
||||
Serializes a point in G2.
|
||||
Returns a bytearray of size 96 as
|
||||
we use the compressed format
|
||||
"""
|
||||
if bls == arkworks_bls or bls == fastest_bls:
|
||||
return bytes(point.to_compressed_bytes())
|
||||
return py_ecc_G2_to_bytes96(point)
|
||||
|
||||
|
||||
def bytes48_to_G1(bytes48):
|
||||
"""
|
||||
Deserializes a purported compressed serialized
|
||||
point in G1.
|
||||
- No subgroup checks are performed
|
||||
- If the bytearray is not a valid serialization
|
||||
of a point in G1, then this method will raise
|
||||
an exception
|
||||
"""
|
||||
if bls == arkworks_bls or bls == fastest_bls:
|
||||
return arkworks_G1.from_compressed_bytes_unchecked(bytes48)
|
||||
return py_ecc_bytes48_to_G1(bytes48)
|
||||
|
||||
|
||||
def bytes96_to_G2(bytes96):
|
||||
"""
|
||||
Deserializes a purported compressed serialized
|
||||
point in G2.
|
||||
- No subgroup checks are performed
|
||||
- If the bytearray is not a valid serialization
|
||||
of a point in G2, then this method will raise
|
||||
an exception
|
||||
"""
|
||||
if bls == arkworks_bls or bls == fastest_bls:
|
||||
return arkworks_G2.from_compressed_bytes_unchecked(bytes96)
|
||||
return py_ecc_bytes96_to_G2(bytes96)
|
||||
|
||||
|
||||
@only_with_bls(alt_return=True)
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
# KZG tests
|
||||
|
||||
A test type for KZG libraries. Tests all the public interfaces that a KZG library required to implement EIP-4844 needs to provide, as defined in `polynomial-commitments.md`.
|
||||
|
||||
We do not recommend rolling your own crypto or using an untested KZG library.
|
||||
|
||||
The KZG test suite runner has the following handlers:
|
||||
|
||||
- [`blob_to_kzg_commitment`](./blob_to_kzg_commitment.md)
|
||||
- [`compute_kzg_proof`](./compute_kzg_proof.md)
|
||||
- [`verify_kzg_proof`](./verify_kzg_proof.md)
|
||||
- [`compute_blob_kzg_proof`](./compute_blob_kzg_proof.md)
|
||||
- [`verify_blob_kzg_proof`](./verify_blob_kzg_proof.md)
|
||||
- [`verify_blob_kzg_proof_batch`](./verify_blob_kzg_proof_batch.md)
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# Test format: Blob to KZG commitment
|
||||
|
||||
Compute the KZG commitment for a given `blob`.
|
||||
|
||||
## Test case format
|
||||
|
||||
The test data is declared in a `data.yaml` file:
|
||||
|
||||
```yaml
|
||||
input:
|
||||
blob: Blob -- the data blob
|
||||
output: KZGCommitment -- The KZG commitment
|
||||
```
|
||||
|
||||
- `blob` here is encoded as a string: hexadecimal encoding of `4096 * 32 = 131072` bytes, prefixed with `0x`.
|
||||
|
||||
All byte(s) fields are encoded as strings, hexadecimal encoding, prefixed with `0x`.
|
||||
|
||||
## Condition
|
||||
|
||||
The `blob_to_kzg_commitment` handler should compute the KZG commitment for `blob`, and the result should match the expected `output`. If the blob is invalid (e.g. incorrect length or one of the 32-byte blocks does not represent a BLS field element) it should error, i.e. the output should be `null`.
|
|
@ -0,0 +1,21 @@
|
|||
# Test format: Compute blob KZG proof
|
||||
|
||||
Compute the blob KZG proof for a given `blob`, that helps with quickly verifying that the KZG commitment for the blob is correct.
|
||||
|
||||
## Test case format
|
||||
|
||||
The test data is declared in a `data.yaml` file:
|
||||
|
||||
```yaml
|
||||
input:
|
||||
blob: Blob -- the data blob
|
||||
output: KZGProof -- The blob KZG proof
|
||||
```
|
||||
|
||||
- `blob` here is encoded as a string: hexadecimal encoding of `4096 * 32 = 131072` bytes, prefixed with `0x`.
|
||||
|
||||
All byte(s) fields are encoded as strings, hexadecimal encoding, prefixed with `0x`.
|
||||
|
||||
## Condition
|
||||
|
||||
The `compute_blob_kzg_proof` handler should compute the blob KZG proof for `blob`, and the result should match the expected `output`. If the blob is invalid (e.g. incorrect length or one of the 32-byte blocks does not represent a BLS field element) it should error, i.e. the output should be `null`.
|
|
@ -0,0 +1,23 @@
|
|||
# Test format: Compute KZG proof
|
||||
|
||||
Compute the KZG proof for a given `blob` and an evaluation point `z`.
|
||||
|
||||
## Test case format
|
||||
|
||||
The test data is declared in a `data.yaml` file:
|
||||
|
||||
```yaml
|
||||
input:
|
||||
blob: Blob -- the data blob representing a polynomial
|
||||
z: Bytes32 -- bytes encoding the BLS field element at which the polynomial should be evaluated
|
||||
output: KZGProof -- The KZG proof
|
||||
```
|
||||
|
||||
- `blob` here is encoded as a string: hexadecimal encoding of `4096 * 32 = 131072` bytes, prefixed with `0x`.
|
||||
- `z` here is encoded as a string: hexadecimal encoding of `32` bytes representing a little endian encoded field element, prefixed with `0x`.
|
||||
|
||||
All byte(s) fields are encoded as strings, hexadecimal encoding, prefixed with `0x`.
|
||||
|
||||
## Condition
|
||||
|
||||
The `compute_kzg_proof` handler should compute the KZG proof for evaluating the polynomial represented by `blob` at `z`, and the result should match the expected `output`. If the blob is invalid (e.g. incorrect length or one of the 32-byte blocks does not represent a BLS field element) or `z` is not a valid BLS field element, it should error, i.e. the output should be `null`.
|
|
@ -0,0 +1,23 @@
|
|||
# Test format: Verify blob KZG proof
|
||||
|
||||
Use the blob KZG proof to verify that the KZG commitment for a given `blob` is correct
|
||||
|
||||
## Test case format
|
||||
|
||||
The test data is declared in a `data.yaml` file:
|
||||
|
||||
```yaml
|
||||
input:
|
||||
blob: Blob -- the data blob
|
||||
commitment: KZGCommitment -- the KZG commitment to the data blob
|
||||
proof: KZGProof -- The KZG proof
|
||||
output: bool -- true (valid proof) or false (incorrect proof)
|
||||
```
|
||||
|
||||
- `blob` here is encoded as a string: hexadecimal encoding of `4096 * 32 = 131072` bytes, prefixed with `0x`.
|
||||
|
||||
All byte(s) fields are encoded as strings, hexadecimal encoding, prefixed with `0x`.
|
||||
|
||||
## Condition
|
||||
|
||||
The `verify_blob_kzg_proof` handler should verify that `commitment` is a correct KZG commitment to `blob` by using the blob KZG proof `proof`, and the result should match the expected `output`. If the commitment or proof is invalid (e.g. not on the curve or not in the G1 subgroup of the BLS curve) or `blob` is invalid (e.g. incorrect length or one of the 32-byte blocks does not represent a BLS field element), it should error, i.e. the output should be `null`.
|
|
@ -0,0 +1,23 @@
|
|||
# Test format: Verify blob KZG proof batch
|
||||
|
||||
Use the blob KZG proofs to verify that the KZG commitments for given `blob`s are correct
|
||||
|
||||
## Test case format
|
||||
|
||||
The test data is declared in a `data.yaml` file:
|
||||
|
||||
```yaml
|
||||
input:
|
||||
blob: List[Blob] -- the data blob
|
||||
commitment: List[KZGCommitment] -- the KZG commitment to the data blob
|
||||
proof: List[KZGProof] -- The KZG proof
|
||||
output: bool -- true (all proofs are valid) or false (some proofs incorrect)
|
||||
```
|
||||
|
||||
- `blob`s here are encoded as a string: hexadecimal encoding of `4096 * 32 = 131072` bytes, prefixed with `0x`.
|
||||
|
||||
All byte(s) fields are encoded as strings, hexadecimal encoding, prefixed with `0x`.
|
||||
|
||||
## Condition
|
||||
|
||||
The `verify_blob_kzg_proof_batch` handler should verify that `commitments` are correct KZG commitments to `blobs` by using the blob KZG proofs `proofs`, and the result should match the expected `output`. If any of the commitments or proofs are invalid (e.g. not on the curve or not in the G1 subgroup of the BLS curve) or any blob is invalid (e.g. incorrect length or one of the 32-byte blocks does not represent a BLS field element), it should error, i.e. the output should be `null`.
|
|
@ -0,0 +1,25 @@
|
|||
# Test format: Verify KZG proof
|
||||
|
||||
Verify the KZG proof for a given `blob` and an evaluation point `z` that claims to result in a value of `y`.
|
||||
|
||||
## Test case format
|
||||
|
||||
The test data is declared in a `data.yaml` file:
|
||||
|
||||
```yaml
|
||||
input:
|
||||
commitment: KZGCommitment -- the KZG commitment to the data blob
|
||||
z: Bytes32 -- bytes encoding the BLS field element at which the polynomial should be evaluated
|
||||
y: Bytes32 -- the claimed result of the evaluation
|
||||
proof: KZGProof -- The KZG proof
|
||||
output: bool -- true (valid proof) or false (incorrect proof)
|
||||
```
|
||||
|
||||
- `z` here is encoded as a string: hexadecimal encoding of `32` bytes representing a little endian encoded field element, prefixed with `0x`.
|
||||
- `y` here is encoded as a string: hexadecimal encoding of `32` bytes representing a little endian encoded field element, prefixed with `0x`.
|
||||
|
||||
All byte(s) fields are encoded as strings, hexadecimal encoding, prefixed with `0x`.
|
||||
|
||||
## Condition
|
||||
|
||||
The `verify_kzg_proof` handler should verify the KZG proof for evaluating the polynomial represented by `blob` at `z` resulting in the value `y`, and the result should match the expected `output`. If the commitment or proof is invalid (e.g. not on the curve or not in the G1 subgroup of the BLS curve) or `z` or `y` are not a valid BLS field element, it should error, i.e. the output should be `null`.
|
|
@ -0,0 +1,3 @@
|
|||
# KZG 4844 Test Generator
|
||||
|
||||
These tests are specific to the KZG API required for implementing EIP-4844
|
|
@ -0,0 +1,579 @@
|
|||
"""
|
||||
KZG 4844 test vectors generator
|
||||
"""
|
||||
|
||||
from hashlib import sha256
|
||||
from typing import Tuple, Iterable, Any, Callable, Dict
|
||||
|
||||
from eth_utils import (
|
||||
encode_hex,
|
||||
int_to_big_endian,
|
||||
)
|
||||
|
||||
from eth2spec.utils import bls
|
||||
from eth2spec.test.helpers.constants import DENEB
|
||||
from eth2spec.test.helpers.typing import SpecForkName
|
||||
from eth2spec.gen_helpers.gen_base import gen_runner, gen_typing
|
||||
from eth2spec.deneb import spec
|
||||
|
||||
|
||||
def expect_exception(func, *args):
|
||||
try:
|
||||
func(*args)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
raise Exception("should have raised exception")
|
||||
|
||||
|
||||
def field_element_bytes(x):
|
||||
return int.to_bytes(x % spec.BLS_MODULUS, 32, "little")
|
||||
|
||||
|
||||
def encode_hex_list(a):
|
||||
return [encode_hex(x) for x in a]
|
||||
|
||||
|
||||
def bls_add_one(x):
|
||||
"""
|
||||
Adds "one" (actually bls.G1()) to a compressed group element.
|
||||
Useful to compute definitely incorrect proofs.
|
||||
"""
|
||||
return bls.G1_to_bytes48(
|
||||
bls.add(bls.bytes48_to_G1(x), bls.G1())
|
||||
)
|
||||
|
||||
|
||||
def evaluate_blob_at(blob, z):
|
||||
return field_element_bytes(
|
||||
spec.evaluate_polynomial_in_evaluation_form(spec.blob_to_polynomial(blob), spec.bytes_to_bls_field(z))
|
||||
)
|
||||
|
||||
|
||||
G1 = bls.G1_to_bytes48(bls.G1())
|
||||
P1_NOT_IN_G1 = bytes.fromhex("8123456789abcdef0123456789abcdef0123456789abcdef" +
|
||||
"0123456789abcdef0123456789abcdef0123456789abcdef")
|
||||
P1_NOT_ON_CURVE = bytes.fromhex("8123456789abcdef0123456789abcdef0123456789abcdef" +
|
||||
"0123456789abcdef0123456789abcdef0123456789abcde0")
|
||||
BLS_MODULUS_BYTES = spec.BLS_MODULUS.to_bytes(32, spec.ENDIANNESS)
|
||||
|
||||
BLOB_ALL_ZEROS = spec.Blob()
|
||||
BLOB_RANDOM_VALID1 = spec.Blob(b''.join([field_element_bytes(pow(2, n + 256, spec.BLS_MODULUS)) for n in range(4096)]))
|
||||
BLOB_RANDOM_VALID2 = spec.Blob(b''.join([field_element_bytes(pow(3, n + 256, spec.BLS_MODULUS)) for n in range(4096)]))
|
||||
BLOB_RANDOM_VALID3 = spec.Blob(b''.join([field_element_bytes(pow(5, n + 256, spec.BLS_MODULUS)) for n in range(4096)]))
|
||||
BLOB_ALL_MODULUS_MINUS_ONE = spec.Blob(b''.join([field_element_bytes(spec.BLS_MODULUS - 1) for n in range(4096)]))
|
||||
BLOB_ALMOST_ZERO = spec.Blob(b''.join([field_element_bytes(1 if n == 3211 else 0) for n in range(4096)]))
|
||||
BLOB_INVALID = spec.Blob(b'\xFF' * 4096 * 32)
|
||||
BLOB_INVALID_CLOSE = spec.Blob(b''.join(
|
||||
[BLS_MODULUS_BYTES if n == 2111 else field_element_bytes(0) for n in range(4096)]
|
||||
))
|
||||
|
||||
VALID_BLOBS = [BLOB_ALL_ZEROS, BLOB_RANDOM_VALID1, BLOB_RANDOM_VALID2,
|
||||
BLOB_RANDOM_VALID3, BLOB_ALL_MODULUS_MINUS_ONE, BLOB_ALMOST_ZERO]
|
||||
INVALID_BLOBS = [BLOB_INVALID, BLOB_INVALID_CLOSE]
|
||||
VALID_ZS = [field_element_bytes(x) for x in [0, 1, 2, pow(5, 1235, spec.BLS_MODULUS),
|
||||
spec.BLS_MODULUS - 1, spec.ROOTS_OF_UNITY[1]]]
|
||||
INVALID_ZS = [x.to_bytes(32, spec.ENDIANNESS) for x in [spec.BLS_MODULUS, 2**256 - 1, 2**256 - 2**128]]
|
||||
|
||||
|
||||
def hash(x):
|
||||
return sha256(x).digest()
|
||||
|
||||
|
||||
def int_to_hex(n: int, byte_length: int = None) -> str:
|
||||
byte_value = int_to_big_endian(n)
|
||||
if byte_length:
|
||||
byte_value = byte_value.rjust(byte_length, b'\x00')
|
||||
return encode_hex(byte_value)
|
||||
|
||||
|
||||
def case01_blob_to_kzg_commitment():
|
||||
# Valid cases
|
||||
for blob in VALID_BLOBS:
|
||||
commitment = spec.blob_to_kzg_commitment(blob)
|
||||
identifier = f'{encode_hex(hash(blob))}'
|
||||
yield f'blob_to_kzg_commitment_case_valid_blob_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
||||
'input': {
|
||||
'blob': encode_hex(blob),
|
||||
},
|
||||
'output': encode_hex(commitment)
|
||||
}
|
||||
|
||||
# Edge case: Invalid blobs
|
||||
for blob in INVALID_BLOBS:
|
||||
identifier = f'{encode_hex(hash(blob))}'
|
||||
expect_exception(spec.blob_to_kzg_commitment, blob)
|
||||
yield f'blob_to_kzg_commitment_case_invalid_blob_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
||||
'input': {
|
||||
'blob': encode_hex(blob)
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
|
||||
def case02_compute_kzg_proof():
|
||||
# Valid cases
|
||||
for blob in VALID_BLOBS:
|
||||
for z in VALID_ZS:
|
||||
proof = spec.compute_kzg_proof(blob, z)
|
||||
identifier = f'{encode_hex(hash(blob))}_{encode_hex(z)}'
|
||||
yield f'compute_kzg_proof_case_valid_blob_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
||||
'input': {
|
||||
'blob': encode_hex(blob),
|
||||
'z': encode_hex(z),
|
||||
},
|
||||
'output': encode_hex(proof)
|
||||
}
|
||||
|
||||
# Edge case: Invalid blobs
|
||||
for blob in INVALID_BLOBS:
|
||||
z = VALID_ZS[0]
|
||||
expect_exception(spec.compute_kzg_proof, blob, z)
|
||||
identifier = f'{encode_hex(hash(blob))}_{encode_hex(z)}'
|
||||
yield f'compute_kzg_proof_case_invalid_blob_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
||||
'input': {
|
||||
'blob': encode_hex(blob),
|
||||
'z': encode_hex(z),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
# Edge case: Invalid z
|
||||
for z in INVALID_ZS:
|
||||
blob = VALID_BLOBS[4]
|
||||
expect_exception(spec.compute_kzg_proof, blob, z)
|
||||
identifier = f'{encode_hex(hash(blob))}_{encode_hex(z)}'
|
||||
yield f'compute_kzg_proof_case_invalid_z_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
||||
'input': {
|
||||
'blob': encode_hex(blob),
|
||||
'z': encode_hex(z),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
|
||||
def case03_verify_kzg_proof():
|
||||
# Valid cases
|
||||
for blob in VALID_BLOBS:
|
||||
for z in VALID_ZS:
|
||||
proof = spec.compute_kzg_proof(blob, z)
|
||||
commitment = spec.blob_to_kzg_commitment(blob)
|
||||
y = evaluate_blob_at(blob, z)
|
||||
assert spec.verify_kzg_proof(commitment, z, y, proof)
|
||||
identifier = f'{encode_hex(hash(blob))}_{encode_hex(z)}'
|
||||
yield f'verify_kzg_proof_case_correct_proof_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
||||
'input': {
|
||||
'commitment': encode_hex(commitment),
|
||||
'z': encode_hex(z),
|
||||
'y': encode_hex(y),
|
||||
'proof': encode_hex(proof),
|
||||
},
|
||||
'output': True
|
||||
}
|
||||
|
||||
# Incorrect proofs
|
||||
for blob in VALID_BLOBS:
|
||||
for z in VALID_ZS:
|
||||
proof = bls_add_one(spec.compute_kzg_proof(blob, z))
|
||||
commitment = spec.blob_to_kzg_commitment(blob)
|
||||
y = evaluate_blob_at(blob, z)
|
||||
assert not spec.verify_kzg_proof(commitment, z, y, proof)
|
||||
identifier = f'{encode_hex(hash(blob))}_{encode_hex(z)}'
|
||||
yield f'verify_kzg_proof_case_incorrect_proof_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
||||
'input': {
|
||||
'commitment': encode_hex(commitment),
|
||||
'z': encode_hex(z),
|
||||
'y': encode_hex(y),
|
||||
'proof': encode_hex(proof),
|
||||
},
|
||||
'output': False
|
||||
}
|
||||
|
||||
# Edge case: Invalid z
|
||||
for z in INVALID_ZS:
|
||||
blob, validz = VALID_BLOBS[4], VALID_ZS[1]
|
||||
proof = spec.compute_kzg_proof(blob, validz)
|
||||
commitment = spec.blob_to_kzg_commitment(blob)
|
||||
y = VALID_ZS[3]
|
||||
expect_exception(spec.verify_kzg_proof, commitment, z, y, proof)
|
||||
identifier = f'{encode_hex(hash(blob))}_{encode_hex(z)}'
|
||||
yield f'verify_kzg_proof_case_invalid_z_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
||||
'input': {
|
||||
'commitment': encode_hex(commitment),
|
||||
'z': encode_hex(z),
|
||||
'y': encode_hex(y),
|
||||
'proof': encode_hex(proof),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
# Edge case: Invalid y
|
||||
blob, z = VALID_BLOBS[1], VALID_ZS[1]
|
||||
proof = spec.compute_kzg_proof(blob, z)
|
||||
commitment = spec.blob_to_kzg_commitment(blob)
|
||||
y = INVALID_ZS[0]
|
||||
expect_exception(spec.verify_kzg_proof, commitment, z, y, proof)
|
||||
yield 'verify_kzg_proof_case_invalid_y', {
|
||||
'input': {
|
||||
'commitment': encode_hex(commitment),
|
||||
'z': encode_hex(z),
|
||||
'y': encode_hex(y),
|
||||
'proof': encode_hex(proof),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
# Edge case: Invalid proof, not in G1
|
||||
blob, z = VALID_BLOBS[2], VALID_ZS[0]
|
||||
proof = P1_NOT_IN_G1
|
||||
commitment = spec.blob_to_kzg_commitment(blob)
|
||||
y = VALID_ZS[1]
|
||||
expect_exception(spec.verify_kzg_proof, commitment, z, y, proof)
|
||||
yield 'verify_kzg_proof_case_proof_not_in_G1', {
|
||||
'input': {
|
||||
'commitment': encode_hex(commitment),
|
||||
'z': encode_hex(z),
|
||||
'y': encode_hex(y),
|
||||
'proof': encode_hex(proof),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
# Edge case: Invalid proof, not on curve
|
||||
blob, z = VALID_BLOBS[3], VALID_ZS[1]
|
||||
proof = P1_NOT_ON_CURVE
|
||||
commitment = spec.blob_to_kzg_commitment(blob)
|
||||
y = VALID_ZS[1]
|
||||
expect_exception(spec.verify_kzg_proof, commitment, z, y, proof)
|
||||
yield 'verify_kzg_proof_case_proof_not_on_curve', {
|
||||
'input': {
|
||||
'commitment': encode_hex(commitment),
|
||||
'z': encode_hex(z),
|
||||
'y': encode_hex(y),
|
||||
'proof': encode_hex(proof),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
# Edge case: Invalid commitment, not in G1
|
||||
blob, z = VALID_BLOBS[4], VALID_ZS[3]
|
||||
proof = spec.compute_kzg_proof(blob, z)
|
||||
commitment = P1_NOT_IN_G1
|
||||
y = VALID_ZS[2]
|
||||
expect_exception(spec.verify_kzg_proof, commitment, z, y, proof)
|
||||
yield 'verify_kzg_proof_case_commitment_not_in_G1', {
|
||||
'input': {
|
||||
'commitment': encode_hex(commitment),
|
||||
'z': encode_hex(z),
|
||||
'y': encode_hex(y),
|
||||
'proof': encode_hex(proof),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
# Edge case: Invalid commitment, not on curve
|
||||
blob, z = VALID_BLOBS[1], VALID_ZS[4]
|
||||
proof = spec.compute_kzg_proof(blob, z)
|
||||
commitment = P1_NOT_ON_CURVE
|
||||
y = VALID_ZS[3]
|
||||
expect_exception(spec.verify_kzg_proof, commitment, z, y, proof)
|
||||
yield 'verify_kzg_proof_case_commitment_not_on_curve', {
|
||||
'input': {
|
||||
'commitment': encode_hex(commitment),
|
||||
'z': encode_hex(z),
|
||||
'y': encode_hex(y),
|
||||
'proof': encode_hex(proof),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
|
||||
def case04_compute_blob_kzg_proof():
|
||||
# Valid cases
|
||||
for blob in VALID_BLOBS:
|
||||
proof = spec.compute_blob_kzg_proof(blob)
|
||||
identifier = f'{encode_hex(hash(blob))}'
|
||||
yield f'compute_blob_kzg_proof_case_valid_blob_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
||||
'input': {
|
||||
'blob': encode_hex(blob),
|
||||
},
|
||||
'output': encode_hex(proof)
|
||||
}
|
||||
|
||||
# Edge case: Invalid blob
|
||||
for blob in INVALID_BLOBS:
|
||||
expect_exception(spec.compute_blob_kzg_proof, blob)
|
||||
identifier = f'{encode_hex(hash(blob))}'
|
||||
yield f'compute_blob_kzg_proof_case_invalid_blob_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
||||
'input': {
|
||||
'blob': encode_hex(blob),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
|
||||
def case05_verify_blob_kzg_proof():
|
||||
# Valid cases
|
||||
for blob in VALID_BLOBS:
|
||||
proof = spec.compute_blob_kzg_proof(blob)
|
||||
commitment = spec.blob_to_kzg_commitment(blob)
|
||||
assert spec.verify_blob_kzg_proof(blob, commitment, proof)
|
||||
identifier = f'{encode_hex(hash(blob))}'
|
||||
yield f'verify_blob_kzg_proof_case_correct_proof_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
||||
'input': {
|
||||
'blob': encode_hex(blob),
|
||||
'commitment': encode_hex(commitment),
|
||||
'proof': encode_hex(proof),
|
||||
},
|
||||
'output': True
|
||||
}
|
||||
|
||||
# Incorrect proofs
|
||||
for blob in VALID_BLOBS:
|
||||
proof = bls_add_one(spec.compute_blob_kzg_proof(blob))
|
||||
commitment = spec.blob_to_kzg_commitment(blob)
|
||||
assert not spec.verify_blob_kzg_proof(blob, commitment, proof)
|
||||
identifier = f'{encode_hex(hash(blob))}'
|
||||
yield f'verify_blob_kzg_proof_case_incorrect_proof_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
||||
'input': {
|
||||
'blob': encode_hex(blob),
|
||||
'commitment': encode_hex(commitment),
|
||||
'proof': encode_hex(proof),
|
||||
},
|
||||
'output': False
|
||||
}
|
||||
|
||||
# Edge case: Invalid proof, not in G1
|
||||
blob = VALID_BLOBS[2]
|
||||
proof = P1_NOT_IN_G1
|
||||
commitment = G1
|
||||
expect_exception(spec.verify_blob_kzg_proof, blob, commitment, proof)
|
||||
yield 'verify_blob_kzg_proof_case_proof_not_in_G1', {
|
||||
'input': {
|
||||
'blob': encode_hex(blob),
|
||||
'commitment': encode_hex(commitment),
|
||||
'proof': encode_hex(proof),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
# Edge case: Invalid proof, not on curve
|
||||
blob = VALID_BLOBS[1]
|
||||
proof = P1_NOT_ON_CURVE
|
||||
commitment = G1
|
||||
expect_exception(spec.verify_blob_kzg_proof, blob, commitment, proof)
|
||||
yield 'verify_blob_kzg_proof_case_proof_not_on_curve', {
|
||||
'input': {
|
||||
'blob': encode_hex(blob),
|
||||
'commitment': encode_hex(commitment),
|
||||
'proof': encode_hex(proof),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
# Edge case: Invalid commitment, not in G1
|
||||
blob = VALID_BLOBS[0]
|
||||
proof = G1
|
||||
commitment = P1_NOT_IN_G1
|
||||
expect_exception(spec.verify_blob_kzg_proof, blob, commitment, proof)
|
||||
yield 'verify_blob_kzg_proof_case_commitment_not_in_G1', {
|
||||
'input': {
|
||||
'blob': encode_hex(blob),
|
||||
'commitment': encode_hex(commitment),
|
||||
'proof': encode_hex(proof),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
# Edge case: Invalid commitment, not on curve
|
||||
blob = VALID_BLOBS[2]
|
||||
proof = G1
|
||||
commitment = P1_NOT_ON_CURVE
|
||||
expect_exception(spec.verify_blob_kzg_proof, blob, commitment, proof)
|
||||
yield 'verify_blob_kzg_proof_case_commitment_not_on_curve', {
|
||||
'input': {
|
||||
'blob': encode_hex(blob),
|
||||
'commitment': encode_hex(commitment),
|
||||
'proof': encode_hex(proof),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
# Edge case: Invalid blob
|
||||
for blob in INVALID_BLOBS:
|
||||
proof = G1
|
||||
commitment = G1
|
||||
expect_exception(spec.verify_blob_kzg_proof, blob, commitment, proof)
|
||||
identifier = f'{encode_hex(hash(blob))}'
|
||||
yield f'verify_blob_kzg_proof_case_invalid_blob_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
||||
'input': {
|
||||
'blob': encode_hex(blob),
|
||||
'commitment': encode_hex(commitment),
|
||||
'proof': encode_hex(proof),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
|
||||
def case06_verify_blob_kzg_proof_batch():
|
||||
# Valid cases
|
||||
proofs = []
|
||||
commitments = []
|
||||
for blob in VALID_BLOBS:
|
||||
proofs.append(spec.compute_blob_kzg_proof(blob))
|
||||
commitments.append(spec.blob_to_kzg_commitment(blob))
|
||||
|
||||
for i in range(len(proofs)):
|
||||
assert spec.verify_blob_kzg_proof_batch(VALID_BLOBS[:i], commitments[:i], proofs[:i])
|
||||
identifier = f'{encode_hex(hash(b"".join(VALID_BLOBS[:i])))}'
|
||||
yield f'verify_blob_kzg_proof_batch_case_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
|
||||
'input': {
|
||||
'blobs': encode_hex_list(VALID_BLOBS[:i]),
|
||||
'commitments': encode_hex_list(commitments[:i]),
|
||||
'proofs': encode_hex_list(proofs[:i]),
|
||||
},
|
||||
'output': True
|
||||
}
|
||||
|
||||
# Incorrect proof
|
||||
proofs_incorrect = [bls_add_one(proofs[0])] + proofs[1:]
|
||||
assert not spec.verify_blob_kzg_proof_batch(VALID_BLOBS, commitments, proofs_incorrect)
|
||||
yield 'verify_blob_kzg_proof_batch_case_invalid_proof', {
|
||||
'input': {
|
||||
'blobs': encode_hex_list(VALID_BLOBS),
|
||||
'commitments': encode_hex_list(commitments),
|
||||
'proofs': encode_hex_list(proofs_incorrect),
|
||||
},
|
||||
'output': False
|
||||
}
|
||||
|
||||
# Edge case: Invalid proof, not in G1
|
||||
proofs_invalid_notG1 = [P1_NOT_IN_G1] + proofs[1:]
|
||||
expect_exception(spec.verify_blob_kzg_proof_batch, VALID_BLOBS, commitments, proofs_invalid_notG1)
|
||||
yield 'verify_blob_kzg_proof_batch_case_proof_not_in_G1', {
|
||||
'input': {
|
||||
'blobs': encode_hex_list(VALID_BLOBS),
|
||||
'commitments': encode_hex_list(commitments),
|
||||
'proofs': encode_hex_list(proofs_invalid_notG1),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
# Edge case: Invalid proof, not on curve
|
||||
proofs_invalid_notCurve = proofs[:1] + [P1_NOT_ON_CURVE] + proofs[2:]
|
||||
expect_exception(spec.verify_blob_kzg_proof_batch, VALID_BLOBS, commitments, proofs_invalid_notCurve)
|
||||
yield 'verify_blob_kzg_proof_batch_case_proof_not_on_curve', {
|
||||
'input': {
|
||||
'blobs': encode_hex_list(VALID_BLOBS),
|
||||
'commitments': encode_hex_list(commitments),
|
||||
'proofs': encode_hex_list(proofs_invalid_notCurve),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
# Edge case: Invalid commitment, not in G1
|
||||
commitments_invalid_notG1 = commitments[:2] + [P1_NOT_IN_G1] + commitments[3:]
|
||||
expect_exception(spec.verify_blob_kzg_proof_batch, VALID_BLOBS, commitments, commitments_invalid_notG1)
|
||||
yield 'verify_blob_kzg_proof_batch_case_commitment_not_in_G1', {
|
||||
'input': {
|
||||
'blobs': encode_hex_list(VALID_BLOBS),
|
||||
'commitments': encode_hex_list(commitments_invalid_notG1),
|
||||
'proofs': encode_hex_list(proofs),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
# Edge case: Invalid commitment, not on curve
|
||||
commitments_invalid_notCurve = commitments[:3] + [P1_NOT_ON_CURVE] + commitments[4:]
|
||||
expect_exception(spec.verify_blob_kzg_proof_batch, VALID_BLOBS, commitments, commitments_invalid_notCurve)
|
||||
yield 'verify_blob_kzg_proof_batch_case_not_on_curve', {
|
||||
'input': {
|
||||
'blobs': encode_hex_list(VALID_BLOBS),
|
||||
'commitments': encode_hex_list(commitments_invalid_notCurve),
|
||||
'proofs': encode_hex_list(proofs),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
# Edge case: Invalid blob
|
||||
blobs_invalid = VALID_BLOBS[:4] + [BLOB_INVALID] + VALID_BLOBS[5:]
|
||||
expect_exception(spec.verify_blob_kzg_proof_batch, blobs_invalid, commitments, proofs)
|
||||
yield 'verify_blob_kzg_proof_batch_case_invalid_blob', {
|
||||
'input': {
|
||||
'blobs': encode_hex_list(blobs_invalid),
|
||||
'commitments': encode_hex_list(commitments),
|
||||
'proofs': encode_hex_list(proofs),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
# Edge case: Blob length different
|
||||
expect_exception(spec.verify_blob_kzg_proof_batch, VALID_BLOBS[:-1], commitments, proofs)
|
||||
yield 'verify_blob_kzg_proof_batch_case_blob_length_different', {
|
||||
'input': {
|
||||
'blobs': encode_hex_list(VALID_BLOBS[:-1]),
|
||||
'commitments': encode_hex_list(commitments),
|
||||
'proofs': encode_hex_list(proofs),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
# Edge case: Commitment length different
|
||||
expect_exception(spec.verify_blob_kzg_proof_batch, VALID_BLOBS, commitments[:-1], proofs)
|
||||
yield 'verify_blob_kzg_proof_batch_case_commitment_length_different', {
|
||||
'input': {
|
||||
'blobs': encode_hex_list(VALID_BLOBS),
|
||||
'commitments': encode_hex_list(commitments[:-1]),
|
||||
'proofs': encode_hex_list(proofs),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
# Edge case: Proof length different
|
||||
expect_exception(spec.verify_blob_kzg_proof_batch, VALID_BLOBS, commitments, proofs[:-1])
|
||||
yield 'verify_blob_kzg_proof_batch_case_proof_length_different', {
|
||||
'input': {
|
||||
'blobs': encode_hex_list(VALID_BLOBS),
|
||||
'commitments': encode_hex_list(commitments),
|
||||
'proofs': encode_hex_list(proofs[:-1]),
|
||||
},
|
||||
'output': None
|
||||
}
|
||||
|
||||
|
||||
def create_provider(fork_name: SpecForkName,
|
||||
handler_name: str,
|
||||
test_case_fn: Callable[[], Iterable[Tuple[str, Dict[str, Any]]]]) -> gen_typing.TestProvider:
|
||||
|
||||
def prepare_fn() -> None:
|
||||
# Nothing to load / change in spec. Maybe in future forks.
|
||||
# Put the tests into the general config category, to not require any particular configuration.
|
||||
return
|
||||
|
||||
def cases_fn() -> Iterable[gen_typing.TestCase]:
|
||||
for data in test_case_fn():
|
||||
(case_name, case_content) = data
|
||||
yield gen_typing.TestCase(
|
||||
fork_name=fork_name,
|
||||
preset_name='general',
|
||||
runner_name='kzg',
|
||||
handler_name=handler_name,
|
||||
suite_name='small',
|
||||
case_name=case_name,
|
||||
case_fn=lambda: [('data', 'data', case_content)]
|
||||
)
|
||||
|
||||
return gen_typing.TestProvider(prepare=prepare_fn, make_cases=cases_fn)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
bls.use_arkworks()
|
||||
gen_runner.run_generator("kzg", [
|
||||
# DENEB
|
||||
create_provider(DENEB, 'blob_to_kzg_commitment', case01_blob_to_kzg_commitment),
|
||||
create_provider(DENEB, 'compute_kzg_proof', case02_compute_kzg_proof),
|
||||
create_provider(DENEB, 'verify_kzg_proof', case03_verify_kzg_proof),
|
||||
create_provider(DENEB, 'compute_blob_kzg_proof', case04_compute_blob_kzg_proof),
|
||||
create_provider(DENEB, 'verify_blob_kzg_proof', case05_verify_blob_kzg_proof),
|
||||
create_provider(DENEB, 'verify_blob_kzg_proof_batch', case06_verify_blob_kzg_proof_batch),
|
||||
])
|
|
@ -0,0 +1,2 @@
|
|||
pytest>=4.4
|
||||
../../../[generator]
|
Loading…
Reference in New Issue