Add initial version of kzg_7594 test generator (#3693)

This commit is contained in:
Justin Traglia 2024-04-24 11:40:27 -05:00 committed by GitHub
parent 731caf8573
commit f5277700e3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 1045 additions and 105 deletions

View File

@ -0,0 +1,155 @@
from hashlib import sha256
from eth_utils import (
encode_hex,
int_to_big_endian,
)
from eth2spec.utils import bls
from eth2spec.eip7594 import spec
###############################################################################
# Helper functions
###############################################################################
def expect_exception(func, *args):
try:
func(*args)
except Exception:
pass
else:
raise Exception("should have raised exception")
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 hash(x):
return sha256(x).digest()
def make_id(*args):
values_str = "_".join(str(arg) for arg in args)
return hash(bytes(values_str, "utf-8"))[:8].hex()
def field_element_bytes(x):
return int.to_bytes(x % spec.BLS_MODULUS, 32, spec.KZG_ENDIANNESS)
def field_element_bytes_unchecked(x):
return int.to_bytes(x, 32, spec.KZG_ENDIANNESS)
def encode_hex_list(a):
return [encode_hex(x) for x in a]
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 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))
)
###############################################################################
# Global variables
###############################################################################
BLS_MODULUS_BYTES = spec.BLS_MODULUS.to_bytes(32, spec.KZG_ENDIANNESS)
# Field Elements
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.compute_roots_of_unity(spec.FIELD_ELEMENTS_PER_BLOB)[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]
# Blobs
BLOB_ALL_ZEROS = spec.Blob()
BLOB_ALL_TWOS = spec.Blob(b''.join([field_element_bytes(2) for n in range(4096)]))
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)]
))
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_ALL_TWOS, BLOB_RANDOM_VALID1, BLOB_RANDOM_VALID2,
BLOB_RANDOM_VALID3, BLOB_ALL_MODULUS_MINUS_ONE, BLOB_ALMOST_ZERO]
INVALID_BLOBS = [BLOB_INVALID, BLOB_INVALID_CLOSE, BLOB_INVALID_LENGTH_PLUS_ONE, BLOB_INVALID_LENGTH_MINUS_ONE]
# Commitments
VALID_COMMITMENTS = [spec.blob_to_kzg_commitment(blob) for blob in VALID_BLOBS]
# Points
G1 = bls.G1_to_bytes48(bls.G1())
G1_INVALID_TOO_FEW_BYTES = G1[:-1]
G1_INVALID_TOO_MANY_BYTES = G1 + b"\x00"
G1_INVALID_P1_NOT_IN_G1 = bytes.fromhex("8123456789abcdef0123456789abcdef0123456789abcdef" +
"0123456789abcdef0123456789abcdef0123456789abcdef")
G1_INVALID_P1_NOT_ON_CURVE = bytes.fromhex("8123456789abcdef0123456789abcdef0123456789abcdef" +
"0123456789abcdef0123456789abcdef0123456789abcde0")
INVALID_G1_POINTS = [G1_INVALID_TOO_FEW_BYTES, G1_INVALID_TOO_MANY_BYTES,
G1_INVALID_P1_NOT_IN_G1, G1_INVALID_P1_NOT_ON_CURVE]
# Individual Cells
CELL_RANDOM_VALID1 = b"".join([field_element_bytes(pow(2, n + 256, spec.BLS_MODULUS))
for n in range(spec.FIELD_ELEMENTS_PER_CELL)])
CELL_RANDOM_VALID2 = b"".join([field_element_bytes(pow(3, n + 256, spec.BLS_MODULUS))
for n in range(spec.FIELD_ELEMENTS_PER_CELL)])
CELL_RANDOM_VALID3 = b"".join([field_element_bytes(pow(5, n + 256, spec.BLS_MODULUS))
for n in range(spec.FIELD_ELEMENTS_PER_CELL)])
CELL_ALL_MAX_VALUE = b"".join([field_element_bytes_unchecked(2 ** 256 - 1)
for n in range(spec.FIELD_ELEMENTS_PER_CELL)])
CELL_ONE_INVALID_FIELD = b"".join([field_element_bytes_unchecked(spec.BLS_MODULUS)
if n == 7 else field_element_bytes(0)
for n in range(spec.FIELD_ELEMENTS_PER_CELL)])
CELL_INVALID_TOO_FEW_BYTES = CELL_RANDOM_VALID1[:-1]
CELL_INVALID_TOO_MANY_BYTES = CELL_RANDOM_VALID2 + b"\x00"
VALID_INDIVIDUAL_RANDOM_CELL_BYTES = [CELL_RANDOM_VALID1, CELL_RANDOM_VALID2, CELL_RANDOM_VALID3]
INVALID_INDIVIDUAL_CELL_BYTES = [CELL_ALL_MAX_VALUE, CELL_ONE_INVALID_FIELD, CELL_INVALID_TOO_FEW_BYTES,
CELL_INVALID_TOO_MANY_BYTES]
# Cells & Proofs
VALID_CELLS_AND_PROOFS = [] # Saved in case02_compute_cells_and_proofs

View File

@ -1,118 +1,37 @@
"""
KZG 4844 test vectors generator
KZG test vectors generator for EIP-4844
"""
from hashlib import sha256
from typing import Tuple, Iterable, Any, Callable, Dict
from eth_utils import (
encode_hex,
int_to_big_endian,
)
from eth_utils import encode_hex
from eth2spec.utils import bls
from eth2spec.deneb import spec
from eth2spec.gen_helpers.gen_base import gen_runner, gen_typing
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
from eth2spec.test.utils.kzg_tests import (
BLOB_ALL_TWOS,
BLOB_ALL_ZEROS,
BLOB_RANDOM_VALID1,
G1,
INVALID_BLOBS,
INVALID_FIELD_ELEMENTS,
INVALID_G1_POINTS,
VALID_BLOBS,
VALID_FIELD_ELEMENTS,
bls_add_one,
encode_hex_list,
expect_exception,
field_element_bytes,
hash,
)
from eth2spec.utils import bls
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, spec.KZG_ENDIANNESS)
def field_element_bytes_unchecked(x):
return int.to_bytes(x, 32, spec.KZG_ENDIANNESS)
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))
)
BLS_MODULUS_BYTES = spec.BLS_MODULUS.to_bytes(32, spec.KZG_ENDIANNESS)
G1 = bls.G1_to_bytes48(bls.G1())
G1_INVALID_TOO_FEW_BYTES = G1[:-1]
G1_INVALID_TOO_MANY_BYTES = G1 + b"\x00"
G1_INVALID_P1_NOT_IN_G1 = bytes.fromhex("8123456789abcdef0123456789abcdef0123456789abcdef" +
"0123456789abcdef0123456789abcdef0123456789abcdef")
G1_INVALID_P1_NOT_ON_CURVE = bytes.fromhex("8123456789abcdef0123456789abcdef0123456789abcdef" +
"0123456789abcdef0123456789abcdef0123456789abcde0")
INVALID_G1_POINTS = [G1_INVALID_TOO_FEW_BYTES, G1_INVALID_TOO_MANY_BYTES,
G1_INVALID_P1_NOT_IN_G1, G1_INVALID_P1_NOT_ON_CURVE]
BLOB_ALL_ZEROS = spec.Blob()
BLOB_ALL_TWOS = spec.Blob(b''.join([field_element_bytes(2) for n in range(4096)]))
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)]
))
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_ALL_TWOS, BLOB_RANDOM_VALID1, BLOB_RANDOM_VALID2,
BLOB_RANDOM_VALID3, BLOB_ALL_MODULUS_MINUS_ONE, BLOB_ALMOST_ZERO]
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.compute_roots_of_unity(spec.FIELD_ELEMENTS_PER_BLOB)[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):
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)
###############################################################################
# Test cases for blob_to_kzg_commitment
###############################################################################
def case01_blob_to_kzg_commitment():
# Valid cases
@ -138,6 +57,10 @@ def case01_blob_to_kzg_commitment():
}
###############################################################################
# Test cases for compute_kzg_proof
###############################################################################
def case02_compute_kzg_proof():
# Valid cases
for blob in VALID_BLOBS:
@ -179,6 +102,10 @@ def case02_compute_kzg_proof():
}
###############################################################################
# Test cases for verify_kzg_proof
###############################################################################
def case03_verify_kzg_proof():
# Valid cases
for blob in VALID_BLOBS:
@ -341,6 +268,10 @@ def case03_verify_kzg_proof():
}
###############################################################################
# Test cases for compute_blob_kzg_proof
###############################################################################
def case04_compute_blob_kzg_proof():
# Valid cases
for blob in VALID_BLOBS:
@ -382,6 +313,10 @@ def case04_compute_blob_kzg_proof():
}
###############################################################################
# Test cases for verify_blob_kzg_proof
###############################################################################
def case05_verify_blob_kzg_proof():
# Valid cases
for blob in VALID_BLOBS:
@ -503,6 +438,10 @@ def case05_verify_blob_kzg_proof():
}
###############################################################################
# Test cases for verify_blob_kzg_proof_batch
###############################################################################
def case06_verify_blob_kzg_proof_batch():
# Valid cases
proofs = []
@ -627,6 +566,10 @@ def case06_verify_blob_kzg_proof_batch():
}
###############################################################################
# Main logic
###############################################################################
def create_provider(fork_name: SpecForkName,
handler_name: str,
test_case_fn: Callable[[], Iterable[Tuple[str, Dict[str, Any]]]]) -> gen_typing.TestProvider:

View File

@ -0,0 +1,3 @@
# KZG Test Generator for EIP-7594
These tests are specific to the API required for implementing PeerDAS polynomial commitment sampling.

View File

@ -0,0 +1,837 @@
"""
KZG test vectors generator for EIP-7594
"""
from typing import Tuple, Iterable, Any, Callable, Dict
from eth_utils import encode_hex
from eth2spec.eip7594 import spec
from eth2spec.gen_helpers.gen_base import gen_runner, gen_typing
from eth2spec.test.helpers.constants import EIP7594
from eth2spec.test.helpers.typing import SpecForkName
from eth2spec.test.utils.kzg_tests import (
BLOB_RANDOM_VALID1,
BLOB_RANDOM_VALID2,
BLOB_RANDOM_VALID3,
CELL_RANDOM_VALID1,
CELL_RANDOM_VALID2,
INVALID_BLOBS,
INVALID_G1_POINTS,
INVALID_INDIVIDUAL_CELL_BYTES,
VALID_BLOBS,
VALID_CELLS_AND_PROOFS,
VALID_COMMITMENTS,
VALID_INDIVIDUAL_RANDOM_CELL_BYTES,
bls_add_one,
encode_hex_list,
expect_exception,
make_id,
)
from eth2spec.utils import bls
###############################################################################
# Test cases for compute_cells
###############################################################################
def case01_compute_cells():
# Valid cases
for blob in VALID_BLOBS:
cells = spec.compute_cells(blob)
identifier = make_id(blob)
yield f'compute_cells_case_valid_{identifier}', {
'input': {
'blob': encode_hex(blob),
},
'output': encode_hex_list(cells)
}
# Edge case: Invalid blobs
for blob in INVALID_BLOBS:
expect_exception(spec.compute_cells, blob)
identifier = make_id(blob)
yield f'compute_cells_case_invalid_blob_{identifier}', {
'input': {
'blob': encode_hex(blob)
},
'output': None
}
###############################################################################
# Test cases for compute_cells_and_proofs
###############################################################################
def case02_compute_cells_and_proofs():
# Valid cases
for blob in VALID_BLOBS:
cells, proofs = spec.compute_cells_and_proofs(blob)
# Save cells & proofs here to save on time.
VALID_CELLS_AND_PROOFS.append((cells, proofs))
identifier = make_id(blob)
yield f'compute_cells_and_proofs_case_valid_{identifier}', {
'input': {
'blob': encode_hex(blob),
},
'output': (encode_hex_list(cells), encode_hex_list(proofs))
}
# Edge case: Invalid blobs
for blob in INVALID_BLOBS:
expect_exception(spec.compute_cells_and_proofs, blob)
identifier = make_id(blob)
yield f'compute_cells_and_proofs_case_invalid_blob_{identifier}', {
'input': {
'blob': encode_hex(blob)
},
'output': None
}
###############################################################################
# Test cases for verify_cell_proof
###############################################################################
def case03_verify_cell_proof():
# Valid cases
for i in range(len(VALID_BLOBS)):
cells, proofs = VALID_CELLS_AND_PROOFS[i]
commitment = VALID_COMMITMENTS[i]
cell_id = (2 ** i - 1) % spec.CELLS_PER_EXT_BLOB
cell = cells[cell_id]
proof = proofs[cell_id]
assert spec.verify_cell_proof(commitment, cell_id, cell, proof)
identifier = make_id(commitment, cell_id, cell, proof)
yield f'verify_cell_proof_case_valid_{identifier}', {
'input': {
'commitment': encode_hex(commitment),
'cell_id': cell_id,
'cell': encode_hex(cell),
'proof': encode_hex(proof),
},
'output': True
}
# Incorrect commitment
for i in range(len(VALID_BLOBS)):
cells, proofs = VALID_CELLS_AND_PROOFS[i]
commitment = bls_add_one(VALID_COMMITMENTS[i])
cell_id = 99 % spec.CELLS_PER_EXT_BLOB
cell = cells[cell_id]
proof = proofs[cell_id]
assert not spec.verify_cell_proof(commitment, cell_id, cell, proof)
identifier = make_id(commitment, cell_id, cell, proof)
yield f'verify_cell_proof_case_incorrect_commitment_{identifier}', {
'input': {
'commitment': encode_hex(commitment),
'cell_id': cell_id,
'cell': encode_hex(cell),
'proof': encode_hex(proof),
},
'output': False
}
# Incorrect cell
for i in range(len(VALID_INDIVIDUAL_RANDOM_CELL_BYTES)):
cell_id = 16 % spec.CELLS_PER_EXT_BLOB
commitment = VALID_COMMITMENTS[i]
cells, proofs = VALID_CELLS_AND_PROOFS[i]
cell = VALID_INDIVIDUAL_RANDOM_CELL_BYTES[i]
proof = proofs[cell_id]
assert not spec.verify_cell_proof(commitment, cell_id, cell, proof)
identifier = make_id(commitment, cell_id, cell, proof)
yield f'verify_cell_proof_case_incorrect_cell_{identifier}', {
'input': {
'commitment': encode_hex(commitment),
'cell_id': cell_id,
'cell': encode_hex(cell),
'proof': encode_hex(proof),
},
'output': False
}
# Incorrect proof
for i in range(len(VALID_BLOBS)):
cell_id = 91 % spec.CELLS_PER_EXT_BLOB
commitment = VALID_COMMITMENTS[i]
cells, proofs = VALID_CELLS_AND_PROOFS[i]
cell = cells[cell_id]
proof = bls_add_one(proofs[cell_id])
assert not spec.verify_cell_proof(commitment, cell_id, cell, proof)
identifier = make_id(commitment, cell_id, cell, proof)
yield f'verify_cell_proof_case_incorrect_proof_{identifier}', {
'input': {
'commitment': encode_hex(commitment),
'cell_id': cell_id,
'cell': encode_hex(cell),
'proof': encode_hex(proof),
},
'output': False
}
# Edge case: Invalid commitment
for commitment in INVALID_G1_POINTS:
cells, proofs = VALID_CELLS_AND_PROOFS[0]
cell_id = 81 % spec.CELLS_PER_EXT_BLOB
cell = cells[cell_id]
proof = proofs[cell_id]
expect_exception(spec.verify_cell_proof, commitment, cell_id, cell, proof)
identifier = make_id(commitment, cell_id, cell, proof)
yield f'verify_cell_proof_case_invalid_commitment_{identifier}', {
'input': {
'commitment': encode_hex(commitment),
'cell_id': cell_id,
'cell': encode_hex(cell),
'proof': encode_hex(proof),
},
'output': None
}
# Edge case: Invalid cell_id
for cell_id in [spec.CELLS_PER_EXT_BLOB, spec.CELLS_PER_EXT_BLOB + 1]:
cells, proofs = VALID_CELLS_AND_PROOFS[1]
commitment = VALID_COMMITMENTS[1]
cell = cells[0]
proof = proofs[0]
expect_exception(spec.verify_cell_proof, commitment, cell_id, cell, proof)
identifier = make_id(commitment, cell_id, cell, proof)
yield f'verify_cell_proof_case_invalid_cell_id_{identifier}', {
'input': {
'commitment': encode_hex(commitment),
'cell_id': cell_id,
'cell': encode_hex(cell),
'proof': encode_hex(proof),
},
'output': None
}
# Edge case: Invalid cell
for cell in INVALID_INDIVIDUAL_CELL_BYTES:
cell_id = 32 % spec.CELLS_PER_EXT_BLOB
commitment = VALID_COMMITMENTS[2]
cells, proofs = VALID_CELLS_AND_PROOFS[2]
proof = proofs[cell_id]
expect_exception(spec.verify_cell_proof, commitment, cell_id, cell, proof)
identifier = make_id(commitment, cell_id, cell, proof)
yield f'verify_cell_proof_case_invalid_cell_{identifier}', {
'input': {
'commitment': encode_hex(commitment),
'cell_id': cell_id,
'cell': encode_hex(cell),
'proof': encode_hex(proof),
},
'output': None
}
# Edge case: Invalid proof
for proof in INVALID_G1_POINTS:
cells, _ = VALID_CELLS_AND_PROOFS[3]
commitment = VALID_COMMITMENTS[3]
cell_id = 36 % spec.CELLS_PER_EXT_BLOB
cell = cells[cell_id]
expect_exception(spec.verify_cell_proof, commitment, cell_id, cell, proof)
identifier = make_id(commitment, cell_id, cell, proof)
yield f'verify_cell_proof_case_invalid_proof_{identifier}', {
'input': {
'commitment': encode_hex(commitment),
'cell_id': cell_id,
'cell': encode_hex(cell),
'proof': encode_hex(proof),
},
'output': None
}
###############################################################################
# Test cases for verify_cell_proof_batch
###############################################################################
def case04_verify_cell_proof_batch():
# Valid cases
for i in range(len(VALID_BLOBS)):
cells, proofs = VALID_CELLS_AND_PROOFS[i]
row_commitments = [VALID_COMMITMENTS[i]]
row_indices = [0] * spec.CELLS_PER_EXT_BLOB
column_indices = list(range(spec.CELLS_PER_EXT_BLOB))
assert spec.verify_cell_proof_batch(row_commitments, row_indices, column_indices, cells, proofs)
identifier = make_id(row_commitments, row_indices, column_indices, cells, proofs)
yield f'verify_cell_proof_batch_case_valid_{identifier}', {
'input': {
'row_commitments': encode_hex_list(row_commitments),
'row_indices': row_indices,
'column_indices': column_indices,
'cells': encode_hex_list(cells),
'proofs': encode_hex_list(proofs),
},
'output': True
}
# Valid: zero cells
cells, row_commitments, row_indices, column_indices, proofs = [], [], [], [], []
assert spec.verify_cell_proof_batch(row_commitments, row_indices, column_indices, cells, proofs)
identifier = make_id(row_commitments, row_indices, column_indices, cells, proofs)
yield f'verify_cell_proof_batch_case_valid_zero_cells_{identifier}', {
'input': {
'row_commitments': encode_hex_list(row_commitments),
'row_indices': row_indices,
'column_indices': column_indices,
'cells': encode_hex_list(cells),
'proofs': encode_hex_list(proofs),
},
'output': True
}
# Valid: Verify cells from multiple blobs
cells0, proofs0 = VALID_CELLS_AND_PROOFS[0]
cells1, proofs1 = VALID_CELLS_AND_PROOFS[1]
row_commitments = VALID_COMMITMENTS[:2]
row_indices = [0, 1]
column_indices = [0, 0]
cells = [cells0[0], cells1[0]]
proofs = [proofs0[0], proofs1[0]]
assert spec.verify_cell_proof_batch(row_commitments, row_indices, column_indices, cells, proofs)
identifier = make_id(row_commitments, row_indices, column_indices, cells, proofs)
yield f'verify_cell_proof_batch_case_valid_multiple_blobs_{identifier}', {
'input': {
'row_commitments': encode_hex_list(row_commitments),
'row_indices': row_indices,
'column_indices': column_indices,
'cells': encode_hex_list(cells),
'proofs': encode_hex_list(proofs),
},
'output': True
}
# Valid: Unused row commitments
cells, proofs = VALID_CELLS_AND_PROOFS[2]
cells, proofs = cells[:3], proofs[:3]
# Provide list of all commitments
row_commitments = VALID_COMMITMENTS
row_indices = [2] * len(cells)
column_indices = list(range(len(cells)))
assert spec.verify_cell_proof_batch(row_commitments, row_indices, column_indices, cells, proofs)
identifier = make_id(row_commitments, row_indices, column_indices, cells, proofs)
yield f'verify_cell_proof_batch_case_valid_unused_row_commitments_{identifier}', {
'input': {
'row_commitments': encode_hex_list(row_commitments),
'row_indices': row_indices,
'column_indices': column_indices,
'cells': encode_hex_list(cells),
'proofs': encode_hex_list(proofs),
},
'output': True
}
# Valid: Same cell multiple times
row_commitments = [VALID_COMMITMENTS[3]]
num_duplicates = 3
row_indices = [0] * num_duplicates
column_indices = [0] * num_duplicates
cells = [VALID_CELLS_AND_PROOFS[3][0][0]] * num_duplicates
proofs = [VALID_CELLS_AND_PROOFS[3][1][0]] * num_duplicates
assert spec.verify_cell_proof_batch(row_commitments, row_indices, column_indices, cells, proofs)
identifier = make_id(row_commitments, row_indices, column_indices, cells, proofs)
yield f'verify_cell_proof_batch_case_valid_same_cell_multiple_times_{identifier}', {
'input': {
'row_commitments': encode_hex_list(row_commitments),
'row_indices': row_indices,
'column_indices': column_indices,
'cells': encode_hex_list(cells),
'proofs': encode_hex_list(proofs),
},
'output': True
}
# Incorrect row commitment
cells, proofs = VALID_CELLS_AND_PROOFS[5]
cells, proofs = cells[:1], proofs[:1]
# Change commitment so it's wrong
row_commitments = [bls_add_one(VALID_COMMITMENTS[5])]
row_indices = [0] * len(cells)
column_indices = list(range(len(cells)))
assert not spec.verify_cell_proof_batch(row_commitments, row_indices, column_indices, cells, proofs)
identifier = make_id(row_commitments, row_indices, column_indices, cells, proofs)
yield f'verify_cell_proof_batch_case_incorrect_row_commitment_{identifier}', {
'input': {
'row_commitments': encode_hex_list(row_commitments),
'row_indices': row_indices,
'column_indices': column_indices,
'cells': encode_hex_list(cells),
'proofs': encode_hex_list(proofs),
},
'output': False
}
# Incorrect cell
cells, proofs = VALID_CELLS_AND_PROOFS[6]
cells, proofs = cells[:1], proofs[:1]
row_commitments = [VALID_COMMITMENTS[6]]
row_indices = [0] * len(cells)
column_indices = list(range(len(cells)))
# Change last cell so it's wrong
cells[-1] = CELL_RANDOM_VALID2
assert not spec.verify_cell_proof_batch(row_commitments, row_indices, column_indices, cells, proofs)
identifier = make_id(row_commitments, row_indices, column_indices, cells, proofs)
yield f'verify_cell_proof_batch_case_incorrect_cell_{identifier}', {
'input': {
'row_commitments': encode_hex_list(row_commitments),
'row_indices': row_indices,
'column_indices': column_indices,
'cells': encode_hex_list(cells),
'proofs': encode_hex_list(proofs),
},
'output': False
}
# Incorrect proof
cells, proofs = VALID_CELLS_AND_PROOFS[0]
cells, proofs = cells[:1], proofs[:1]
row_commitments = [VALID_COMMITMENTS[0]]
row_indices = [0] * len(cells)
column_indices = list(range(len(cells)))
# Change last proof so it's wrong
proofs[-1] = bls_add_one(proofs[-1])
assert not spec.verify_cell_proof_batch(row_commitments, row_indices, column_indices, cells, proofs)
identifier = make_id(row_commitments, row_indices, column_indices, cells, proofs)
yield f'verify_cell_proof_batch_case_incorrect_proof_{identifier}', {
'input': {
'row_commitments': encode_hex_list(row_commitments),
'row_indices': row_indices,
'column_indices': column_indices,
'cells': encode_hex_list(cells),
'proofs': encode_hex_list(proofs),
},
'output': False
}
# Edge case: Invalid row commitment
for i, commitment in enumerate(INVALID_G1_POINTS):
cells, proofs = VALID_CELLS_AND_PROOFS[i % len(INVALID_G1_POINTS)]
cells, proofs = cells[:1], proofs[:1]
# Set row_commitments to the invalid commitment
row_commitments = [commitment]
row_indices = [0] * len(cells)
column_indices = list(range(len(cells)))
expect_exception(spec.verify_cell_proof_batch, row_commitments, row_indices, column_indices, cells, proofs)
identifier = make_id(row_commitments, row_indices, column_indices, cells, proofs)
yield f'verify_cell_proof_batch_case_invalid_row_commitment_{identifier}', {
'input': {
'row_commitments': encode_hex_list(row_commitments),
'row_indices': row_indices,
'column_indices': column_indices,
'cells': encode_hex_list(cells),
'proofs': encode_hex_list(proofs),
},
'output': None
}
# Edge case: Invalid row_index
cells, proofs = VALID_CELLS_AND_PROOFS[0]
cells, proofs = cells[:1], proofs[:1]
row_commitments = [VALID_COMMITMENTS[0]]
row_indices = [0] * len(cells)
# Set first row index to an invalid value
row_indices[0] = 1
column_indices = list(range(len(cells)))
expect_exception(spec.verify_cell_proof_batch, row_commitments, row_indices, column_indices, cells, proofs)
identifier = make_id(row_commitments, row_indices, column_indices, cells, proofs)
yield f'verify_cell_proof_batch_case_invalid_row_index_{identifier}', {
'input': {
'row_commitments': encode_hex_list(row_commitments),
'row_indices': row_indices,
'column_indices': column_indices,
'cells': encode_hex_list(cells),
'proofs': encode_hex_list(proofs),
},
'output': None
}
# Edge case: Invalid column_index
cells, proofs = VALID_CELLS_AND_PROOFS[1]
cells, proofs = cells[:1], proofs[:1]
row_commitments = [VALID_COMMITMENTS[1]]
row_indices = [0] * len(cells)
column_indices = list(range(len(cells)))
# Set first column index to an invalid value
column_indices[0] = spec.CELLS_PER_EXT_BLOB
expect_exception(spec.verify_cell_proof_batch, row_commitments, row_indices, column_indices, cells, proofs)
identifier = make_id(row_commitments, row_indices, column_indices, cells, proofs)
yield f'verify_cell_proof_batch_case_invalid_column_index_{identifier}', {
'input': {
'row_commitments': encode_hex_list(row_commitments),
'row_indices': row_indices,
'column_indices': column_indices,
'cells': encode_hex_list(cells),
'proofs': encode_hex_list(proofs),
},
'output': None
}
# Edge case: Invalid cell
for i, cell in enumerate(INVALID_INDIVIDUAL_CELL_BYTES):
cells, proofs = VALID_CELLS_AND_PROOFS[i % len(INVALID_INDIVIDUAL_CELL_BYTES)]
cells, proofs = cells[:1], proofs[:1]
row_commitments = [VALID_COMMITMENTS[i % len(INVALID_INDIVIDUAL_CELL_BYTES)]]
row_indices = [0] * len(cells)
column_indices = list(range(len(cells)))
# Set first cell to the invalid cell
cells[0] = cell
expect_exception(spec.verify_cell_proof_batch, row_commitments, row_indices, column_indices, cells, proofs)
identifier = make_id(row_commitments, row_indices, column_indices, cells, proofs)
yield f'verify_cell_proof_batch_case_invalid_cell_{identifier}', {
'input': {
'row_commitments': encode_hex_list(row_commitments),
'row_indices': row_indices,
'column_indices': column_indices,
'cells': encode_hex_list(cells),
'proofs': encode_hex_list(proofs),
},
'output': None
}
# Edge case: Invalid proof
for i, proof in enumerate(INVALID_G1_POINTS):
cells, proofs = VALID_CELLS_AND_PROOFS[i % len(INVALID_G1_POINTS)]
cells, proofs = cells[:1], proofs[:1]
row_commitments = [VALID_COMMITMENTS[i % len(INVALID_G1_POINTS)]]
row_indices = [0] * len(cells)
column_indices = list(range(len(cells)))
# Set first proof to the invalid proof
proofs[0] = proof
expect_exception(spec.verify_cell_proof_batch, row_commitments, row_indices, column_indices, cells, proofs)
identifier = make_id(row_commitments, row_indices, column_indices, cells, proofs)
yield f'verify_cell_proof_batch_case_invalid_proof_{identifier}', {
'input': {
'row_commitments': encode_hex_list(row_commitments),
'row_indices': row_indices,
'column_indices': column_indices,
'cells': encode_hex_list(cells),
'proofs': encode_hex_list(proofs),
},
'output': None
}
# Edge case: Missing a row commitment
cells, proofs = VALID_CELLS_AND_PROOFS[0]
cells, proofs = cells[:1], proofs[:1]
# Do not include the row commitment
row_commitments = []
row_indices = [0] * len(cells)
column_indices = list(range(len(cells)))
expect_exception(spec.verify_cell_proof_batch, row_commitments, row_indices, column_indices, cells, proofs)
identifier = make_id(row_commitments, row_indices, column_indices, cells, proofs)
yield f'verify_cell_proof_batch_case_invalid_missing_row_commitment_{identifier}', {
'input': {
'row_commitments': encode_hex_list(row_commitments),
'row_indices': row_indices,
'column_indices': column_indices,
'cells': encode_hex_list(cells),
'proofs': encode_hex_list(proofs),
},
'output': None
}
# Edge case: Missing a row index
cells, proofs = VALID_CELLS_AND_PROOFS[1]
cells, proofs = cells[:2], proofs[:2]
row_commitments = [VALID_COMMITMENTS[1]]
# Leave off one of the row indices
row_indices = [0] * (len(cells) - 1)
column_indices = list(range(len(cells)))
expect_exception(spec.verify_cell_proof_batch, row_commitments, row_indices, column_indices, cells, proofs)
identifier = make_id(row_commitments, row_indices, column_indices, cells, proofs)
yield f'verify_cell_proof_batch_case_invalid_missing_row_index_{identifier}', {
'input': {
'row_commitments': encode_hex_list(row_commitments),
'row_indices': row_indices,
'column_indices': column_indices,
'cells': encode_hex_list(cells),
'proofs': encode_hex_list(proofs),
},
'output': None
}
# Edge case: Missing a column index
cells, proofs = VALID_CELLS_AND_PROOFS[2]
cells, proofs = cells[:2], proofs[:2]
row_commitments = [VALID_COMMITMENTS[2]]
row_indices = [0] * len(cells)
# Leave off one of the column indices
column_indices = list(range(len(cells) - 1))
expect_exception(spec.verify_cell_proof_batch, row_commitments, row_indices, column_indices, cells, proofs)
identifier = make_id(row_commitments, row_indices, column_indices, cells, proofs)
yield f'verify_cell_proof_batch_case_invalid_missing_column_index_{identifier}', {
'input': {
'row_commitments': encode_hex_list(row_commitments),
'row_indices': row_indices,
'column_indices': column_indices,
'cells': encode_hex_list(cells),
'proofs': encode_hex_list(proofs),
},
'output': None
}
# Edge case: Missing a cell
cells, proofs = VALID_CELLS_AND_PROOFS[3]
cells, proofs = cells[:2], proofs[:2]
row_commitments = [VALID_COMMITMENTS[3]]
row_indices = [0] * len(cells)
column_indices = list(range(len(cells)))
# Remove the last proof
cells = cells[:-1]
expect_exception(spec.verify_cell_proof_batch, row_commitments, row_indices, column_indices, cells, proofs)
identifier = make_id(row_commitments, row_indices, column_indices, cells, proofs)
yield f'verify_cell_proof_batch_case_invalid_missing_cell_{identifier}', {
'input': {
'row_commitments': encode_hex_list(row_commitments),
'row_indices': row_indices,
'column_indices': column_indices,
'cells': encode_hex_list(cells),
'proofs': encode_hex_list(proofs),
},
'output': None
}
# Edge case: Missing a proof
cells, proofs = VALID_CELLS_AND_PROOFS[4]
cells, proofs = cells[:2], proofs[:2]
row_commitments = [VALID_COMMITMENTS[4]]
row_indices = [0] * len(cells)
column_indices = list(range(len(cells)))
# Remove the last proof
proofs = proofs[:-1]
expect_exception(spec.verify_cell_proof_batch, row_commitments, row_indices, column_indices, cells, proofs)
identifier = make_id(row_commitments, row_indices, column_indices, cells, proofs)
yield f'verify_cell_proof_batch_case_invalid_missing_proof_{identifier}', {
'input': {
'row_commitments': encode_hex_list(row_commitments),
'row_indices': row_indices,
'column_indices': column_indices,
'cells': encode_hex_list(cells),
'proofs': encode_hex_list(proofs),
},
'output': None
}
###############################################################################
# Test cases for recover_all_cells
###############################################################################
def case05_recover_all_cells():
# Valid: No missing cells
blob = BLOB_RANDOM_VALID1
cells = spec.compute_cells(blob)
cell_ids = list(range(spec.CELLS_PER_EXT_BLOB))
recovered_cells = spec.recover_all_cells(cell_ids, cells)
assert recovered_cells == cells
identifier = make_id(cell_ids, cells)
yield f'recover_all_cells_case_valid_no_missing_{identifier}', {
'input': {
'cell_ids': cell_ids,
'cells': encode_hex_list(cells),
},
'output': encode_hex_list(recovered_cells)
}
# Valid: Half missing cells (every other cell)
blob = BLOB_RANDOM_VALID2
cells = spec.compute_cells(blob)
cell_ids = list(range(0, spec.CELLS_PER_EXT_BLOB, 2))
partial_cells = [cells[cell_id] for cell_id in cell_ids]
recovered_cells = spec.recover_all_cells(cell_ids, partial_cells)
assert recovered_cells == cells
identifier = make_id(cell_ids, partial_cells)
yield f'recover_all_cells_case_valid_half_missing_every_other_cell_{identifier}', {
'input': {
'cell_ids': cell_ids,
'cells': encode_hex_list(partial_cells),
},
'output': encode_hex_list(recovered_cells)
}
# Valid: Half missing cells (first half)
blob = BLOB_RANDOM_VALID3
cells = spec.compute_cells(blob)
cell_ids = list(range(0, spec.CELLS_PER_EXT_BLOB // 2))
partial_cells = [cells[cell_id] for cell_id in cell_ids]
recovered_cells = spec.recover_all_cells(cell_ids, partial_cells)
assert recovered_cells == cells
identifier = make_id(cell_ids, partial_cells)
yield f'recover_all_cells_case_valid_half_missing_first_half_{identifier}', {
'input': {
'cell_ids': cell_ids,
'cells': encode_hex_list(partial_cells),
},
'output': encode_hex_list(recovered_cells)
}
# Valid: Half missing cells (second half)
blob = BLOB_RANDOM_VALID1
cells = spec.compute_cells(blob)
cell_ids = list(range(spec.CELLS_PER_EXT_BLOB // 2, spec.CELLS_PER_EXT_BLOB))
partial_cells = [cells[cell_id] for cell_id in cell_ids]
recovered_cells = spec.recover_all_cells(cell_ids, partial_cells)
assert recovered_cells == cells
identifier = make_id(cell_ids, partial_cells)
yield f'recover_all_cells_case_valid_half_missing_second_half_{identifier}', {
'input': {
'cell_ids': cell_ids,
'cells': encode_hex_list(partial_cells),
},
'output': encode_hex_list(recovered_cells)
}
# Edge case: All cells are missing
cell_ids, partial_cells = [], []
expect_exception(spec.recover_all_cells, cell_ids, partial_cells)
identifier = make_id(cell_ids, partial_cells)
yield f'recover_all_cells_case_invalid_all_cells_are_missing_{identifier}', {
'input': {
'cell_ids': cell_ids,
'cells': encode_hex_list(partial_cells),
},
'output': None
}
# Edge case: More than half missing
blob = BLOB_RANDOM_VALID2
cells = spec.compute_cells(blob)
cell_ids = list(range(spec.CELLS_PER_EXT_BLOB // 2 - 1))
partial_cells = [cells[cell_id] for cell_id in cell_ids]
expect_exception(spec.recover_all_cells, cell_ids, partial_cells)
identifier = make_id(cell_ids, partial_cells)
yield f'recover_all_cells_case_invalid_more_than_half_missing_{identifier}', {
'input': {
'cell_ids': cell_ids,
'cells': encode_hex_list(partial_cells),
},
'output': None
}
# Edge case: Invalid cell_id
blob = BLOB_RANDOM_VALID1
cells = spec.compute_cells(blob)
cell_ids = list(range(spec.CELLS_PER_EXT_BLOB // 2))
partial_cells = [cells[cell_id] for cell_id in cell_ids]
# Replace first cell_id with an invalid value
cell_ids[0] = spec.CELLS_PER_EXT_BLOB
expect_exception(spec.recover_all_cells, cell_ids, partial_cells)
identifier = make_id(cell_ids, partial_cells)
yield f'recover_all_cells_case_invalid_cell_id_{identifier}', {
'input': {
'cell_ids': cell_ids,
'cells': encode_hex_list(partial_cells),
},
'output': None
}
# Edge case: Invalid cell
blob = BLOB_RANDOM_VALID2
for cell in INVALID_INDIVIDUAL_CELL_BYTES:
cells = spec.compute_cells(blob)
cell_ids = list(range(spec.CELLS_PER_EXT_BLOB // 2))
partial_cells = [cells[cell_id] for cell_id in cell_ids]
# Replace first cell with an invalid value
partial_cells[0] = cell
expect_exception(spec.recover_all_cells, cell_ids, partial_cells)
identifier = make_id(cell_ids, partial_cells)
yield f'recover_all_cells_case_invalid_cell_{identifier}', {
'input': {
'cell_ids': cell_ids,
'cells': encode_hex_list(partial_cells),
},
'output': None
}
# Edge case: More cell_ids than cells
blob = BLOB_RANDOM_VALID3
cells = spec.compute_cells(blob)
cell_ids = list(range(0, spec.CELLS_PER_EXT_BLOB, 2))
partial_cells = [cells[cell_id] for cell_id in cell_ids]
# Add another cell_id
cell_ids.append(spec.CELLS_PER_EXT_BLOB - 1)
expect_exception(spec.recover_all_cells, cell_ids, partial_cells)
identifier = make_id(cell_ids, partial_cells)
yield f'recover_all_cells_case_invalid_more_cell_ids_than_cells_{identifier}', {
'input': {
'cell_ids': cell_ids,
'cells': encode_hex_list(partial_cells),
},
'output': None
}
# Edge case: More cells than cell_ids
blob = BLOB_RANDOM_VALID1
cells = spec.compute_cells(blob)
cell_ids = list(range(0, spec.CELLS_PER_EXT_BLOB, 2))
partial_cells = [cells[cell_id] for cell_id in cell_ids]
# Add another cell
partial_cells.append(CELL_RANDOM_VALID1)
expect_exception(spec.recover_all_cells, cell_ids, partial_cells)
identifier = make_id(cell_ids, partial_cells)
yield f'recover_all_cells_case_invalid_more_cells_than_cell_ids_{identifier}', {
'input': {
'cell_ids': cell_ids,
'cells': encode_hex_list(partial_cells),
},
'output': None
}
# Edge case: Duplicate cell_id
blob = BLOB_RANDOM_VALID2
cells = spec.compute_cells(blob)
cell_ids = list(range(spec.CELLS_PER_EXT_BLOB // 2))
partial_cells = [cells[cell_id] for cell_id in cell_ids]
# Replace first cell_id with the second cell_id
cell_ids[0] = cell_ids[1]
expect_exception(spec.recover_all_cells, cell_ids, partial_cells)
identifier = make_id(cell_ids, partial_cells)
yield f'recover_all_cells_case_invalid_duplicate_cell_id_{identifier}', {
'input': {
'cell_ids': cell_ids,
'cells': encode_hex_list(partial_cells),
},
'output': None
}
###############################################################################
# Main logic
###############################################################################
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='kzg-mainnet',
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_7594", [
# EIP-7594
create_provider(EIP7594, 'compute_cells', case01_compute_cells),
create_provider(EIP7594, 'compute_cells_and_proofs', case02_compute_cells_and_proofs),
create_provider(EIP7594, 'verify_cell_proof', case03_verify_cell_proof),
create_provider(EIP7594, 'verify_cell_proof_batch', case04_verify_cell_proof_batch),
create_provider(EIP7594, 'recover_all_cells', case05_recover_all_cells),
])

View File

@ -0,0 +1,2 @@
pytest>=4.4
../../../[generator]