Add KZG tests for input length inputs (#3282)

This commit is contained in:
Justin Traglia 2023-03-07 14:56:55 -07:00 committed by GitHub
parent 15033d28b9
commit ccfe576dcc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 260 additions and 44 deletions

View File

@ -72,7 +72,10 @@ Public functions MUST accept raw bytes as input and perform the required cryptog
| Name | Value | Notes |
| - | - | - |
| `BLS_MODULUS` | `52435875175126190479447740508185965837690552500527637822603658699938581184513` | Scalar field modulus of BLS12-381 |
| `BYTES_PER_COMMITMENT` | `uint64(48)` | The number of bytes in a KZG commitment |
| `BYTES_PER_PROOF` | `uint64(48)` | The number of bytes in a KZG proof |
| `BYTES_PER_FIELD_ELEMENT` | `uint64(32)` | Bytes used to encode a BLS scalar field element |
| `BYTES_PER_BLOB` | `uint64(BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB)` | The number of bytes in a blob |
| `G1_POINT_AT_INFINITY` | `Bytes48(b'\xc0' + b'\x00' * 47)` | Serialized form of the point at infinity on the G1 group |
@ -340,6 +343,7 @@ def blob_to_kzg_commitment(blob: Blob) -> KZGCommitment:
"""
Public method.
"""
assert len(blob) == BYTES_PER_BLOB
return g1_lincomb(bit_reversal_permutation(KZG_SETUP_LAGRANGE), blob_to_polynomial(blob))
```
@ -347,17 +351,22 @@ def blob_to_kzg_commitment(blob: Blob) -> KZGCommitment:
```python
def verify_kzg_proof(commitment_bytes: Bytes48,
z: Bytes32,
y: Bytes32,
z_bytes: Bytes32,
y_bytes: Bytes32,
proof_bytes: Bytes48) -> bool:
"""
Verify KZG proof that ``p(z) == y`` where ``p(z)`` is the polynomial represented by ``polynomial_kzg``.
Receives inputs as bytes.
Public method.
"""
assert len(commitment_bytes) == BYTES_PER_COMMITMENT
assert len(z_bytes) == BYTES_PER_FIELD_ELEMENT
assert len(y_bytes) == BYTES_PER_FIELD_ELEMENT
assert len(proof_bytes) == BYTES_PER_PROOF
return verify_kzg_proof_impl(bytes_to_kzg_commitment(commitment_bytes),
bytes_to_bls_field(z),
bytes_to_bls_field(y),
bytes_to_bls_field(z_bytes),
bytes_to_bls_field(y_bytes),
bytes_to_kzg_proof(proof_bytes))
```
@ -431,14 +440,16 @@ def verify_kzg_proof_batch(commitments: Sequence[KZGCommitment],
#### `compute_kzg_proof`
```python
def compute_kzg_proof(blob: Blob, z: Bytes32) -> Tuple[KZGProof, Bytes32]:
def compute_kzg_proof(blob: Blob, z_bytes: Bytes32) -> Tuple[KZGProof, Bytes32]:
"""
Compute KZG proof at point `z` for the polynomial represented by `blob`.
Do this by computing the quotient polynomial in evaluation form: q(x) = (p(x) - p(z)) / (x - z).
Public method.
"""
assert len(blob) == BYTES_PER_BLOB
assert len(z_bytes) == BYTES_PER_FIELD_ELEMENT
polynomial = blob_to_polynomial(blob)
proof, y = compute_kzg_proof_impl(polynomial, bytes_to_bls_field(z))
proof, y = compute_kzg_proof_impl(polynomial, bytes_to_bls_field(z_bytes))
return proof, y.to_bytes(BYTES_PER_FIELD_ELEMENT, ENDIANNESS)
```
@ -509,6 +520,8 @@ def compute_blob_kzg_proof(blob: Blob, commitment_bytes: Bytes48) -> KZGProof:
This method does not verify that the commitment is correct with respect to `blob`.
Public method.
"""
assert len(blob) == BYTES_PER_BLOB
assert len(commitment_bytes) == BYTES_PER_COMMITMENT
commitment = bytes_to_kzg_commitment(commitment_bytes)
polynomial = blob_to_polynomial(blob)
evaluation_challenge = compute_challenge(blob, commitment)
@ -527,6 +540,10 @@ def verify_blob_kzg_proof(blob: Blob,
Public method.
"""
assert len(blob) == BYTES_PER_BLOB
assert len(commitment_bytes) == BYTES_PER_COMMITMENT
assert len(proof_bytes) == BYTES_PER_PROOF
commitment = bytes_to_kzg_commitment(commitment_bytes)
polynomial = blob_to_polynomial(blob)
@ -556,6 +573,9 @@ def verify_blob_kzg_proof_batch(blobs: Sequence[Blob],
commitments, evaluation_challenges, ys, proofs = [], [], [], []
for blob, commitment_bytes, proof_bytes in zip(blobs, commitments_bytes, proofs_bytes):
assert len(blob) == BYTES_PER_BLOB
assert len(commitment_bytes) == BYTES_PER_COMMITMENT
assert len(proof_bytes) == BYTES_PER_PROOF
commitment = bytes_to_kzg_commitment(commitment_bytes)
commitments.append(commitment)
polynomial = blob_to_polynomial(blob)

View File

@ -27,7 +27,11 @@ def expect_exception(func, *args):
def field_element_bytes(x):
return int.to_bytes(x % spec.BLS_MODULUS, 32, "little")
return int.to_bytes(x % spec.BLS_MODULUS, 32, spec.ENDIANNESS)
def field_element_bytes_unchecked(x):
return int.to_bytes(x, 32, spec.ENDIANNESS)
def encode_hex_list(a):
@ -67,13 +71,30 @@ 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)]
))
BLOB_INVALID_LENGTH_PLUS_ONE = BLOB_RANDOM_VALID1 + b"\x00"
BLOB_INVALID_LENGTH_MINUS_ONE = BLOB_RANDOM_VALID1[:-1]
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]]
INVALID_BLOBS = [BLOB_INVALID, BLOB_INVALID_CLOSE, BLOB_INVALID_LENGTH_PLUS_ONE, BLOB_INVALID_LENGTH_MINUS_ONE]
FE_VALID1 = field_element_bytes(0)
FE_VALID2 = field_element_bytes(1)
FE_VALID3 = field_element_bytes(2)
FE_VALID4 = field_element_bytes(pow(5, 1235, spec.BLS_MODULUS))
FE_VALID5 = field_element_bytes(spec.BLS_MODULUS - 1)
FE_VALID6 = field_element_bytes(spec.ROOTS_OF_UNITY[1])
VALID_FIELD_ELEMENTS = [FE_VALID1, FE_VALID2, FE_VALID3, FE_VALID4, FE_VALID5, FE_VALID6]
FE_INVALID_EQUAL_TO_MODULUS = field_element_bytes_unchecked(spec.BLS_MODULUS)
FE_INVALID_MODULUS_PLUS_ONE = field_element_bytes_unchecked(spec.BLS_MODULUS + 1)
FE_INVALID_UINT256_MAX = field_element_bytes_unchecked(2**256 - 1)
FE_INVALID_UINT256_MID = field_element_bytes_unchecked(2**256 - 2**128)
FE_INVALID_LENGTH_PLUS_ONE = VALID_FIELD_ELEMENTS[0] + b"\x00"
FE_INVALID_LENGTH_MINUS_ONE = VALID_FIELD_ELEMENTS[0][:-1]
INVALID_FIELD_ELEMENTS = [FE_INVALID_EQUAL_TO_MODULUS, FE_INVALID_MODULUS_PLUS_ONE,
FE_INVALID_UINT256_MAX, FE_INVALID_UINT256_MID,
FE_INVALID_LENGTH_PLUS_ONE, FE_INVALID_LENGTH_MINUS_ONE]
def hash(x):
@ -114,7 +135,7 @@ def case01_blob_to_kzg_commitment():
def case02_compute_kzg_proof():
# Valid cases
for blob in VALID_BLOBS:
for z in VALID_ZS:
for z in VALID_FIELD_ELEMENTS:
proof, y = 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()}', {
@ -127,7 +148,7 @@ def case02_compute_kzg_proof():
# Edge case: Invalid blobs
for blob in INVALID_BLOBS:
z = VALID_ZS[0]
z = VALID_FIELD_ELEMENTS[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()}', {
@ -139,7 +160,7 @@ def case02_compute_kzg_proof():
}
# Edge case: Invalid z
for z in INVALID_ZS:
for z in INVALID_FIELD_ELEMENTS:
blob = VALID_BLOBS[4]
expect_exception(spec.compute_kzg_proof, blob, z)
identifier = f'{encode_hex(hash(blob))}_{encode_hex(z)}'
@ -155,7 +176,7 @@ def case02_compute_kzg_proof():
def case03_verify_kzg_proof():
# Valid cases
for blob in VALID_BLOBS:
for z in VALID_ZS:
for z in VALID_FIELD_ELEMENTS:
proof, y = spec.compute_kzg_proof(blob, z)
commitment = spec.blob_to_kzg_commitment(blob)
assert spec.verify_kzg_proof(commitment, z, y, proof)
@ -172,7 +193,7 @@ def case03_verify_kzg_proof():
# Incorrect proofs
for blob in VALID_BLOBS:
for z in VALID_ZS:
for z in VALID_FIELD_ELEMENTS:
proof_orig, y = spec.compute_kzg_proof(blob, z)
proof = bls_add_one(proof_orig)
commitment = spec.blob_to_kzg_commitment(blob)
@ -189,8 +210,8 @@ def case03_verify_kzg_proof():
}
# Edge case: Invalid z
for z in INVALID_ZS:
blob, validz = VALID_BLOBS[4], VALID_ZS[1]
for z in INVALID_FIELD_ELEMENTS:
blob, validz = VALID_BLOBS[4], VALID_FIELD_ELEMENTS[1]
proof, y = spec.compute_kzg_proof(blob, validz)
commitment = spec.blob_to_kzg_commitment(blob)
expect_exception(spec.verify_kzg_proof, commitment, z, y, proof)
@ -206,26 +227,27 @@ def case03_verify_kzg_proof():
}
# 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
}
for y in INVALID_FIELD_ELEMENTS:
blob, z = VALID_BLOBS[4], VALID_FIELD_ELEMENTS[1]
proof, _ = spec.compute_kzg_proof(blob, z)
commitment = spec.blob_to_kzg_commitment(blob)
expect_exception(spec.verify_kzg_proof, commitment, z, y, proof)
identifier = f'{encode_hex(hash(blob))}_{encode_hex(y)}'
yield f'verify_kzg_proof_case_invalid_y_{(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 proof, not in G1
blob, z = VALID_BLOBS[2], VALID_ZS[0]
blob, z = VALID_BLOBS[2], VALID_FIELD_ELEMENTS[0]
proof = P1_NOT_IN_G1
commitment = spec.blob_to_kzg_commitment(blob)
y = VALID_ZS[1]
y = VALID_FIELD_ELEMENTS[1]
expect_exception(spec.verify_kzg_proof, commitment, z, y, proof)
yield 'verify_kzg_proof_case_proof_not_in_G1', {
'input': {
@ -238,10 +260,10 @@ def case03_verify_kzg_proof():
}
# Edge case: Invalid proof, not on curve
blob, z = VALID_BLOBS[3], VALID_ZS[1]
blob, z = VALID_BLOBS[3], VALID_FIELD_ELEMENTS[1]
proof = P1_NOT_ON_CURVE
commitment = spec.blob_to_kzg_commitment(blob)
y = VALID_ZS[1]
y = VALID_FIELD_ELEMENTS[1]
expect_exception(spec.verify_kzg_proof, commitment, z, y, proof)
yield 'verify_kzg_proof_case_proof_not_on_curve', {
'input': {
@ -253,8 +275,42 @@ def case03_verify_kzg_proof():
'output': None
}
# Edge case: Invalid proof, too few bytes
blob = VALID_BLOBS[1]
commitment = spec.blob_to_kzg_commitment(blob)
z = VALID_FIELD_ELEMENTS[4]
proof, y = spec.compute_kzg_proof(blob, z)
proof = proof[:-1]
expect_exception(spec.verify_kzg_proof, commitment, z, y, proof)
yield 'verify_kzg_proof_case_proof_too_few_bytes', {
'input': {
'commitment': encode_hex(commitment),
'z': encode_hex(z),
'y': encode_hex(y),
'proof': encode_hex(proof),
},
'output': None
}
# Edge case: Invalid proof, too many bytes
blob = VALID_BLOBS[1]
commitment = spec.blob_to_kzg_commitment(blob)
z = VALID_FIELD_ELEMENTS[4]
proof, y = spec.compute_kzg_proof(blob, z)
proof = proof + b"\x00"
expect_exception(spec.verify_kzg_proof, commitment, z, y, proof)
yield 'verify_kzg_proof_case_proof_too_many_bytes', {
'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]
blob, z = VALID_BLOBS[4], VALID_FIELD_ELEMENTS[3]
proof, y = spec.compute_kzg_proof(blob, z)
commitment = P1_NOT_IN_G1
expect_exception(spec.verify_kzg_proof, commitment, z, y, proof)
@ -269,7 +325,7 @@ def case03_verify_kzg_proof():
}
# Edge case: Invalid commitment, not on curve
blob, z = VALID_BLOBS[1], VALID_ZS[4]
blob, z = VALID_BLOBS[1], VALID_FIELD_ELEMENTS[4]
proof, y = spec.compute_kzg_proof(blob, z)
commitment = P1_NOT_ON_CURVE
expect_exception(spec.verify_kzg_proof, commitment, z, y, proof)
@ -283,6 +339,38 @@ def case03_verify_kzg_proof():
'output': None
}
# Edge case: Invalid commitment, too few bytes
blob = VALID_BLOBS[1]
commitment = spec.blob_to_kzg_commitment(blob)[:-1]
z = VALID_FIELD_ELEMENTS[4]
proof, y = spec.compute_kzg_proof(blob, z)
expect_exception(spec.verify_kzg_proof, commitment, z, y, proof)
yield 'verify_kzg_proof_case_commitment_too_few_bytes', {
'input': {
'commitment': encode_hex(commitment),
'z': encode_hex(z),
'y': encode_hex(y),
'proof': encode_hex(proof),
},
'output': None
}
# Edge case: Invalid commitment, too many bytes
blob = VALID_BLOBS[1]
commitment = spec.blob_to_kzg_commitment(blob) + b"\x00"
z = VALID_FIELD_ELEMENTS[4]
proof, y = spec.compute_kzg_proof(blob, z)
expect_exception(spec.verify_kzg_proof, commitment, z, y, proof)
yield 'verify_kzg_proof_case_commitment_too_many_bytes', {
'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
@ -397,6 +485,34 @@ def case05_verify_blob_kzg_proof():
'output': None
}
# Edge case: Invalid proof, too few bytes
blob = VALID_BLOBS[1]
commitment = spec.blob_to_kzg_commitment(blob)
proof = spec.compute_blob_kzg_proof(blob, commitment)[:-1]
expect_exception(spec.verify_blob_kzg_proof, blob, commitment, proof)
yield 'verify_blob_kzg_proof_case_proof_too_few_bytes', {
'input': {
'blob': encode_hex(blob),
'commitment': encode_hex(commitment),
'proof': encode_hex(proof),
},
'output': None
}
# Edge case: Invalid proof, too many bytes
blob = VALID_BLOBS[1]
commitment = spec.blob_to_kzg_commitment(blob)
proof = spec.compute_blob_kzg_proof(blob, commitment) + b"\x00"
expect_exception(spec.verify_blob_kzg_proof, blob, commitment, proof)
yield 'verify_blob_kzg_proof_case_proof_too_many_bytes', {
'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
@ -425,6 +541,36 @@ def case05_verify_blob_kzg_proof():
'output': None
}
# Edge case: Invalid commitment, too few bytes
blob = VALID_BLOBS[1]
commitment = spec.blob_to_kzg_commitment(blob)
proof = spec.compute_blob_kzg_proof(blob, commitment)
commitment = commitment[:-1]
expect_exception(spec.verify_blob_kzg_proof, blob, commitment, proof)
yield 'verify_blob_kzg_proof_case_commitment_too_few_bytes', {
'input': {
'blob': encode_hex(blob),
'commitment': encode_hex(commitment),
'proof': encode_hex(proof),
},
'output': None
}
# Edge case: Invalid commitment, too many bytes
blob = VALID_BLOBS[1]
commitment = spec.blob_to_kzg_commitment(blob)
proof = spec.compute_blob_kzg_proof(blob, commitment)
commitment = commitment + b"\x00"
expect_exception(spec.verify_blob_kzg_proof, blob, commitment, proof)
yield 'verify_blob_kzg_proof_case_commitment_too_many_bytes', {
'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
@ -473,6 +619,20 @@ def case06_verify_blob_kzg_proof_batch():
'output': False
}
# Edge case: Invalid blobs
for blob in INVALID_BLOBS:
blobs_invalid = VALID_BLOBS[:4] + [blob] + VALID_BLOBS[5:]
expect_exception(spec.verify_blob_kzg_proof_batch, blobs_invalid, commitments, proofs)
identifier = f'{encode_hex(hash(blob))}'
yield f'verify_blob_kzg_proof_batch_case_invalid_blob_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
'input': {
'blobs': encode_hex_list(blobs_invalid),
'commitments': encode_hex_list(commitments),
'proofs': encode_hex_list(proofs),
},
'output': None
}
# 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)
@ -497,6 +657,30 @@ def case06_verify_blob_kzg_proof_batch():
'output': None
}
# Edge case: Invalid proof, too few bytes
proofs_invalid_tooFewBytes = proofs[:1] + [proofs[1][:-1]] + proofs[2:]
expect_exception(spec.verify_blob_kzg_proof_batch, VALID_BLOBS, commitments, proofs_invalid_tooFewBytes)
yield 'verify_blob_kzg_proof_batch_case_proof_too_few_bytes', {
'input': {
'blobs': encode_hex_list(VALID_BLOBS),
'commitments': encode_hex_list(commitments),
'proofs': encode_hex_list(proofs_invalid_tooFewBytes),
},
'output': None
}
# Edge case: Invalid proof, too many bytes
proofs_invalid_tooManyBytes = proofs[:1] + [proofs[1] + b"\x00"] + proofs[2:]
expect_exception(spec.verify_blob_kzg_proof_batch, VALID_BLOBS, commitments, proofs_invalid_tooManyBytes)
yield 'verify_blob_kzg_proof_batch_case_proof_too_many_bytes', {
'input': {
'blobs': encode_hex_list(VALID_BLOBS),
'commitments': encode_hex_list(commitments),
'proofs': encode_hex_list(proofs_invalid_tooManyBytes),
},
'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)
@ -521,13 +705,25 @@ def case06_verify_blob_kzg_proof_batch():
'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', {
# Edge case: Invalid commitment, too few bytes
commitments_invalid_tooFewBytes = commitments[:3] + [commitments[3][:-1]] + commitments[4:]
expect_exception(spec.verify_blob_kzg_proof_batch, VALID_BLOBS, commitments, commitments_invalid_tooFewBytes)
yield 'verify_blob_kzg_proof_batch_case_too_few_bytes', {
'input': {
'blobs': encode_hex_list(blobs_invalid),
'commitments': encode_hex_list(commitments),
'blobs': encode_hex_list(VALID_BLOBS),
'commitments': encode_hex_list(commitments_invalid_tooFewBytes),
'proofs': encode_hex_list(proofs),
},
'output': None
}
# Edge case: Invalid commitment, too many bytes
commitments_invalid_tooManyBytes = commitments[:3] + [commitments[3] + b"\x00"] + commitments[4:]
expect_exception(spec.verify_blob_kzg_proof_batch, VALID_BLOBS, commitments, commitments_invalid_tooManyBytes)
yield 'verify_blob_kzg_proof_batch_case_too_many_bytes', {
'input': {
'blobs': encode_hex_list(VALID_BLOBS),
'commitments': encode_hex_list(commitments_invalid_tooManyBytes),
'proofs': encode_hex_list(proofs),
},
'output': None