Merge branch 'dev' into sf-epochoverrides

This commit is contained in:
Etan Kissling 2022-12-05 16:22:24 +01:00
commit 8524f54433
No known key found for this signature in database
GPG Key ID: B21DA824C5A3D03D
12 changed files with 116 additions and 67 deletions

View File

@ -105,12 +105,12 @@ install_test:
# Testing against `minimal` config by default # Testing against `minimal` config by default
test: pyspec test: pyspec
. venv/bin/activate; cd $(PY_SPEC_DIR); \ . venv/bin/activate; cd $(PY_SPEC_DIR); \
python3 -m pytest -n 4 --disable-bls --cov=eth2spec.phase0.minimal --cov=eth2spec.altair.minimal --cov=eth2spec.bellatrix.minimal --cov=eth2spec.capella.minimal --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec python3 -m pytest -n 4 --disable-bls --cov=eth2spec.phase0.minimal --cov=eth2spec.altair.minimal --cov=eth2spec.bellatrix.minimal --cov=eth2spec.capella.minimal --cov=eth2spec.eip4844.minimal --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec
# Testing against `minimal` config by default # Testing against `minimal` config by default
find_test: pyspec find_test: pyspec
. venv/bin/activate; cd $(PY_SPEC_DIR); \ . venv/bin/activate; cd $(PY_SPEC_DIR); \
python3 -m pytest -k=$(K) --disable-bls --cov=eth2spec.phase0.minimal --cov=eth2spec.altair.minimal --cov=eth2spec.bellatrix.minimal --cov=eth2spec.capella.minimal --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec python3 -m pytest -k=$(K) --disable-bls --cov=eth2spec.phase0.minimal --cov=eth2spec.altair.minimal --cov=eth2spec.bellatrix.minimal --cov=eth2spec.capella.minimal --cov=eth2spec.eip4844.minimal --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec
citest: pyspec citest: pyspec
mkdir -p $(TEST_REPORT_DIR); mkdir -p $(TEST_REPORT_DIR);
@ -142,8 +142,8 @@ codespell:
lint: pyspec lint: pyspec
. venv/bin/activate; cd $(PY_SPEC_DIR); \ . venv/bin/activate; cd $(PY_SPEC_DIR); \
flake8 --config $(LINTER_CONFIG_FILE) ./eth2spec \ flake8 --config $(LINTER_CONFIG_FILE) ./eth2spec \
&& pylint --disable=all --enable unused-argument ./eth2spec/phase0 ./eth2spec/altair ./eth2spec/bellatrix ./eth2spec/capella \ && pylint --rcfile $(LINTER_CONFIG_FILE) ./eth2spec/phase0 ./eth2spec/altair ./eth2spec/bellatrix ./eth2spec/capella ./eth2spec/eip4844 \
&& mypy --config-file $(LINTER_CONFIG_FILE) -p eth2spec.phase0 -p eth2spec.altair -p eth2spec.bellatrix -p eth2spec.capella && mypy --config-file $(LINTER_CONFIG_FILE) -p eth2spec.phase0 -p eth2spec.altair -p eth2spec.bellatrix -p eth2spec.capella -p eth2spec.eip4844
lint_generators: pyspec lint_generators: pyspec
. venv/bin/activate; cd $(TEST_GENERATORS_DIR); \ . venv/bin/activate; cd $(TEST_GENERATORS_DIR); \

View File

@ -24,7 +24,7 @@ Features are researched and developed in parallel, and then consolidated into se
### In-development Specifications ### In-development Specifications
| Code Name or Topic | Specs | Notes | | Code Name or Topic | Specs | Notes |
| - | - | - | | - | - | - |
| Capella (tentative) | <ul><li>Core</li><ul><li>[Beacon chain changes](specs/capella/beacon-chain.md)</li><li>[Capella fork](specs/capella/fork.md)</li></ul><li>Additions</li><ul><li>[Validator additions](specs/capella/validator.md)</li></ul></ul> | | Capella (tentative) | <ul><li>Core</li><ul><li>[Beacon chain changes](specs/capella/beacon-chain.md)</li><li>[Capella fork](specs/capella/fork.md)</li></ul><li>Additions</li><ul><li>[Validator additions](specs/capella/validator.md)</li><li>[P2P networking](specs/capella/p2p-interface.md)</li></ul></ul> |
| EIP4844 (tentative) | <ul><li>Core</li><ul><li>[Beacon Chain changes](specs/eip4844/beacon-chain.md)</li><li>[EIP-4844 fork](specs/eip4844/fork.md)</li><li>[Polynomial commitments](specs/eip4844/polynomial-commitments.md)</li></ul><li>Additions</li><ul><li>[Honest validator guide changes](specs/eip4844/validator.md)</li><li>[P2P networking](specs/eip4844/p2p-interface.md)</li></ul></ul> | | EIP4844 (tentative) | <ul><li>Core</li><ul><li>[Beacon Chain changes](specs/eip4844/beacon-chain.md)</li><li>[EIP-4844 fork](specs/eip4844/fork.md)</li><li>[Polynomial commitments](specs/eip4844/polynomial-commitments.md)</li></ul><li>Additions</li><ul><li>[Honest validator guide changes](specs/eip4844/validator.md)</li><li>[P2P networking](specs/eip4844/p2p-interface.md)</li></ul></ul> |
| Sharding (outdated) | <ul><li>Core</li><ul><li>[Beacon Chain changes](specs/sharding/beacon-chain.md)</li></ul><li>Additions</li><ul><li>[P2P networking](specs/sharding/p2p-interface.md)</li></ul></ul> | | Sharding (outdated) | <ul><li>Core</li><ul><li>[Beacon Chain changes](specs/sharding/beacon-chain.md)</li></ul><li>Additions</li><ul><li>[P2P networking](specs/sharding/p2p-interface.md)</li></ul></ul> |
| Custody Game (outdated) | <ul><li>Core</li><ul><li>[Beacon Chain changes](specs/custody_game/beacon-chain.md)</li></ul><li>Additions</li><ul><li>[Honest validator guide changes](specs/custody_game/validator.md)</li></ul></ul> | Dependent on sharding | | Custody Game (outdated) | <ul><li>Core</li><ul><li>[Beacon Chain changes](specs/custody_game/beacon-chain.md)</li></ul><li>Additions</li><ul><li>[Honest validator guide changes](specs/custody_game/validator.md)</li></ul></ul> | Dependent on sharding |

View File

@ -11,3 +11,8 @@ warn_unused_configs = True
warn_redundant_casts = True warn_redundant_casts = True
ignore_missing_imports = True ignore_missing_imports = True
# pylint
[MESSAGES CONTROL]
disable = all
enable = unused-argument

View File

@ -588,6 +588,7 @@ class NoopExecutionEngine(ExecutionEngine):
pass pass
def get_payload(self: ExecutionEngine, payload_id: PayloadId) -> ExecutionPayload: def get_payload(self: ExecutionEngine, payload_id: PayloadId) -> ExecutionPayload:
# pylint: disable=unused-argument
raise NotImplementedError("no default block production") raise NotImplementedError("no default block production")
@ -643,12 +644,14 @@ T = TypeVar('T') # For generic function
def no_op(fn): # type: ignore def no_op(fn): # type: ignore
# pylint: disable=unused-argument
def wrapper(*args, **kw): # type: ignore def wrapper(*args, **kw): # type: ignore
return None return None
return wrapper return wrapper
def get_empty_list_result(fn): # type: ignore def get_empty_list_result(fn): # type: ignore
# pylint: disable=unused-argument
def wrapper(*args, **kw): # type: ignore def wrapper(*args, **kw): # type: ignore
return [] return []
return wrapper return wrapper
@ -663,7 +666,8 @@ get_expected_withdrawals = get_empty_list_result(get_expected_withdrawals)
# End # End
# #
def retrieve_blobs_sidecar(slot: Slot, beacon_block_root: Root) -> Optional[BlobsSidecar]: def retrieve_blobs_sidecar(slot: Slot, beacon_block_root: Root) -> PyUnion[BlobsSidecar, str]:
# pylint: disable=unused-argument
return "TEST"''' return "TEST"'''
@classmethod @classmethod
@ -682,8 +686,8 @@ spec_builders = {
} }
def is_spec_defined_type(value: str) -> bool: def is_byte_vector(value: str) -> bool:
return value.startswith(('ByteList', 'Union', 'Vector', 'List')) return value.startswith(('ByteVector'))
def objects_to_spec(preset_name: str, def objects_to_spec(preset_name: str,
@ -696,17 +700,8 @@ def objects_to_spec(preset_name: str,
new_type_definitions = ( new_type_definitions = (
'\n\n'.join( '\n\n'.join(
[ [
f"class {key}({value}):\n pass\n" f"class {key}({value}):\n pass\n" if not is_byte_vector(value) else f"class {key}({value}): # type: ignore\n pass\n"
for key, value in spec_object.custom_types.items() for key, value in spec_object.custom_types.items()
if not is_spec_defined_type(value)
]
)
+ ('\n\n' if len([key for key, value in spec_object.custom_types.items() if is_spec_defined_type(value)]) > 0 else '')
+ '\n\n'.join(
[
f"{key} = {value}\n"
for key, value in spec_object.custom_types.items()
if is_spec_defined_type(value)
] ]
) )
) )

View File

@ -112,10 +112,10 @@ Up to `MAX_BLS_TO_EXECUTION_CHANGES`, [`BLSToExecutionChange`](./beacon-chain.md
## Enabling validator withdrawals ## Enabling validator withdrawals
Validator balances are fully or partially withdrawn via an automatic process. Validator balances are withdrawn periodically via an automatic process. For exited validators, the full balance is withdrawn. For active validators, the balance in excess of `MAX_EFFECTIVE_BALANCE` is withdrawn.
For validators, there is one prerequisite for this automated process: There is one prerequisite for this automated process:
withdrawal credentials pointing to an execution layer address, i.e. having an `ETH1_ADDRESS_WITHDRAWAL_PREFIX`. the validator's withdrawal credentials pointing to an execution layer address, i.e. having an `ETH1_ADDRESS_WITHDRAWAL_PREFIX`.
If a validator has a `BLS_WITHDRAWAL_PREFIX` withdrawal credential prefix, to participate in withdrawals the validator must If a validator has a `BLS_WITHDRAWAL_PREFIX` withdrawal credential prefix, to participate in withdrawals the validator must
create a one-time message to change their withdrawal credential from the version authenticated with a BLS key to the create a one-time message to change their withdrawal credential from the version authenticated with a BLS key to the

View File

@ -55,7 +55,7 @@ This upgrade adds blobs to the beacon chain as part of EIP-4844. This is an exte
| Name | Value | | Name | Value |
| - | - | | - | - |
| `BLOB_TX_TYPE` | `uint8(0x05)` | | `BLOB_TX_TYPE` | `uint8(0x05)` |
| `VERSIONED_HASH_VERSION_KZG` | `Bytes1(0x01)` | | `VERSIONED_HASH_VERSION_KZG` | `Bytes1('0x01')` |
## Preset ## Preset
@ -175,10 +175,13 @@ but MUST NOT be considered valid until a valid `BlobsSidecar` has been downloade
def is_data_available(slot: Slot, beacon_block_root: Root, blob_kzg_commitments: Sequence[KZGCommitment]) -> bool: def is_data_available(slot: Slot, beacon_block_root: Root, blob_kzg_commitments: Sequence[KZGCommitment]) -> bool:
# `retrieve_blobs_sidecar` is implementation dependent, raises an exception if not available. # `retrieve_blobs_sidecar` is implementation dependent, raises an exception if not available.
sidecar = retrieve_blobs_sidecar(slot, beacon_block_root) sidecar = retrieve_blobs_sidecar(slot, beacon_block_root)
if sidecar == "TEST":
return True # For testing; remove once we have a way to inject `BlobsSidecar` into tests
validate_blobs_sidecar(slot, beacon_block_root, blob_kzg_commitments, sidecar)
# For testing, `retrieve_blobs_sidecar` returns "TEST.
# TODO: Remove it once we have a way to inject `BlobsSidecar` into tests.
if isinstance(sidecar, str):
return True
validate_blobs_sidecar(slot, beacon_block_root, blob_kzg_commitments, sidecar)
return True return True
``` ```
@ -216,7 +219,7 @@ def tx_peek_blob_versioned_hashes(opaque_tx: Transaction) -> Sequence[VersionedH
```python ```python
def verify_kzg_commitments_against_transactions(transactions: Sequence[Transaction], def verify_kzg_commitments_against_transactions(transactions: Sequence[Transaction],
kzg_commitments: Sequence[KZGCommitment]) -> bool: kzg_commitments: Sequence[KZGCommitment]) -> bool:
all_versioned_hashes = [] all_versioned_hashes: List[VersionedHash] = []
for tx in transactions: for tx in transactions:
if tx[0] == BLOB_TX_TYPE: if tx[0] == BLOB_TX_TYPE:
all_versioned_hashes += tx_peek_blob_versioned_hashes(tx) all_versioned_hashes += tx_peek_blob_versioned_hashes(tx)
@ -283,7 +286,8 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe
#### Blob KZG commitments #### Blob KZG commitments
```python ```python
def process_blob_kzg_commitments(state: BeaconState, body: BeaconBlockBody): def process_blob_kzg_commitments(state: BeaconState, body: BeaconBlockBody) -> None:
# pylint: disable=unused-argument
assert verify_kzg_commitments_against_transactions(body.execution_payload.transactions, body.blob_kzg_commitments) assert verify_kzg_commitments_against_transactions(body.execution_payload.transactions, body.blob_kzg_commitments)
``` ```

View File

@ -19,9 +19,10 @@
- [`reverse_bits`](#reverse_bits) - [`reverse_bits`](#reverse_bits)
- [`bit_reversal_permutation`](#bit_reversal_permutation) - [`bit_reversal_permutation`](#bit_reversal_permutation)
- [BLS12-381 helpers](#bls12-381-helpers) - [BLS12-381 helpers](#bls12-381-helpers)
- [`hash_to_bls_field`](#hash_to_bls_field)
- [`bytes_to_bls_field`](#bytes_to_bls_field) - [`bytes_to_bls_field`](#bytes_to_bls_field)
- [`blob_to_polynomial`](#blob_to_polynomial) - [`blob_to_polynomial`](#blob_to_polynomial)
- [`hash_to_bls_field`](#hash_to_bls_field) - [`compute_challenges`](#compute_challenges)
- [`bls_modular_inverse`](#bls_modular_inverse) - [`bls_modular_inverse`](#bls_modular_inverse)
- [`div`](#div) - [`div`](#div)
- [`g1_lincomb`](#g1_lincomb) - [`g1_lincomb`](#g1_lincomb)
@ -41,7 +42,6 @@
<!-- END doctoc generated TOC please keep comment here to allow auto update --> <!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC --> <!-- /TOC -->
## Introduction ## Introduction
This document specifies basic polynomial operations and KZG polynomial commitment operations as they are needed for the EIP-4844 specification. The implementations are not optimized for performance, but readability. All practical implementations should optimize the polynomial operations. This document specifies basic polynomial operations and KZG polynomial commitment operations as they are needed for the EIP-4844 specification. The implementations are not optimized for performance, but readability. All practical implementations should optimize the polynomial operations.
@ -138,14 +138,29 @@ def bit_reversal_permutation(sequence: Sequence[T]) -> Sequence[T]:
### BLS12-381 helpers ### BLS12-381 helpers
#### `hash_to_bls_field`
```python
def hash_to_bls_field(data: bytes) -> BLSFieldElement:
"""
Hash ``data`` and convert the output to a BLS scalar field element.
The output is not uniform over the BLS field.
"""
hashed_data = hash(data)
return BLSFieldElement(int.from_bytes(hashed_data, ENDIANNESS) % BLS_MODULUS)
```
#### `bytes_to_bls_field` #### `bytes_to_bls_field`
```python ```python
def bytes_to_bls_field(b: Bytes32) -> BLSFieldElement: def bytes_to_bls_field(b: Bytes32) -> BLSFieldElement:
""" """
Convert 32-byte value to a BLS field scalar. The output is not uniform over the BLS field. Convert 32-byte value to a BLS scalar field element.
This function does not accept inputs greater than the BLS modulus.
""" """
return int.from_bytes(b, ENDIANNESS) % BLS_MODULUS field_element = int.from_bytes(b, ENDIANNESS)
assert field_element < BLS_MODULUS
return BLSFieldElement(field_element)
``` ```
#### `blob_to_polynomial` #### `blob_to_polynomial`
@ -157,37 +172,49 @@ def blob_to_polynomial(blob: Blob) -> Polynomial:
""" """
polynomial = Polynomial() polynomial = Polynomial()
for i in range(FIELD_ELEMENTS_PER_BLOB): for i in range(FIELD_ELEMENTS_PER_BLOB):
value = int.from_bytes(blob[i * BYTES_PER_FIELD_ELEMENT: (i + 1) * BYTES_PER_FIELD_ELEMENT], ENDIANNESS) value = bytes_to_bls_field(blob[i * BYTES_PER_FIELD_ELEMENT: (i + 1) * BYTES_PER_FIELD_ELEMENT])
assert value < BLS_MODULUS
polynomial[i] = value polynomial[i] = value
return polynomial return polynomial
``` ```
#### `hash_to_bls_field` #### `compute_challenges`
```python ```python
def hash_to_bls_field(polys: Sequence[Polynomial], def compute_challenges(polynomials: Sequence[Polynomial],
comms: Sequence[KZGCommitment]) -> BLSFieldElement: commitments: Sequence[KZGCommitment]) -> Tuple[Sequence[BLSFieldElement], BLSFieldElement]:
""" """
Compute 32-byte hash of serialized polynomials and commitments concatenated. Return the Fiat-Shamir challenges required by the rest of the protocol.
This hash is then converted to a BLS field element, where the result is not uniform over the BLS field. The Fiat-Shamir logic works as per the following pseudocode:
Return the BLS field element.
hashed_data = hash(DOMAIN_SEPARATOR, polynomials, commitments)
r = hash(hashed_data, 0)
r_powers = [1, r, r**2, r**3, ...]
eval_challenge = hash(hashed_data, 1)
Then return `r_powers` and `eval_challenge` after converting them to BLS field elements.
The resulting field elements are not uniform over the BLS field.
""" """
# Append the number of polynomials and the degree of each polynomial as a domain separator # Append the number of polynomials and the degree of each polynomial as a domain separator
num_polys = int.to_bytes(len(polys), 8, ENDIANNESS) num_polynomials = int.to_bytes(len(polynomials), 8, ENDIANNESS)
degree_poly = int.to_bytes(FIELD_ELEMENTS_PER_BLOB, 8, ENDIANNESS) degree_poly = int.to_bytes(FIELD_ELEMENTS_PER_BLOB, 8, ENDIANNESS)
data = FIAT_SHAMIR_PROTOCOL_DOMAIN + degree_poly + num_polys data = FIAT_SHAMIR_PROTOCOL_DOMAIN + degree_poly + num_polynomials
# Append each polynomial which is composed by field elements # Append each polynomial which is composed by field elements
for poly in polys: for poly in polynomials:
for field_element in poly: for field_element in poly:
data += int.to_bytes(field_element, BYTES_PER_FIELD_ELEMENT, ENDIANNESS) data += int.to_bytes(field_element, BYTES_PER_FIELD_ELEMENT, ENDIANNESS)
# Append serialized G1 points # Append serialized G1 points
for commitment in comms: for commitment in commitments:
data += commitment data += commitment
return bytes_to_bls_field(hash(data)) # Transcript has been prepared: time to create the challenges
hashed_data = hash(data)
r = hash_to_bls_field(hashed_data + b'\x00')
r_powers = compute_powers(r, len(commitments))
eval_challenge = hash_to_bls_field(hashed_data + b'\x01')
return r_powers, eval_challenge
``` ```
#### `bls_modular_inverse` #### `bls_modular_inverse`
@ -198,7 +225,7 @@ def bls_modular_inverse(x: BLSFieldElement) -> BLSFieldElement:
Compute the modular inverse of x Compute the modular inverse of x
i.e. return y such that x * y % BLS_MODULUS == 1 and return 0 for x == 0 i.e. return y such that x * y % BLS_MODULUS == 1 and return 0 for x == 0
""" """
return pow(x, -1, BLS_MODULUS) if x != 0 else 0 return BLSFieldElement(pow(x, -1, BLS_MODULUS)) if x != 0 else BLSFieldElement(0)
``` ```
#### `div` #### `div`
@ -208,7 +235,7 @@ def div(x: BLSFieldElement, y: BLSFieldElement) -> BLSFieldElement:
""" """
Divide two field elements: ``x`` by `y``. Divide two field elements: ``x`` by `y``.
""" """
return (int(x) * int(bls_modular_inverse(y))) % BLS_MODULUS return BLSFieldElement((int(x) * int(bls_modular_inverse(y))) % BLS_MODULUS)
``` ```
#### `g1_lincomb` #### `g1_lincomb`
@ -234,11 +261,12 @@ def poly_lincomb(polys: Sequence[Polynomial],
Given a list of ``polynomials``, interpret it as a 2D matrix and compute the linear combination Given a list of ``polynomials``, interpret it as a 2D matrix and compute the linear combination
of each column with `scalars`: return the resulting polynomials. of each column with `scalars`: return the resulting polynomials.
""" """
result = [0] * len(polys[0]) assert len(polys) == len(scalars)
result = [0] * FIELD_ELEMENTS_PER_BLOB
for v, s in zip(polys, scalars): for v, s in zip(polys, scalars):
for i, x in enumerate(v): for i, x in enumerate(v):
result[i] = (result[i] + int(s) * int(x)) % BLS_MODULUS result[i] = (result[i] + int(s) * int(x)) % BLS_MODULUS
return [BLSFieldElement(x) for x in result] return Polynomial([BLSFieldElement(x) for x in result])
``` ```
#### `compute_powers` #### `compute_powers`
@ -246,7 +274,7 @@ def poly_lincomb(polys: Sequence[Polynomial],
```python ```python
def compute_powers(x: BLSFieldElement, n: uint64) -> Sequence[BLSFieldElement]: def compute_powers(x: BLSFieldElement, n: uint64) -> Sequence[BLSFieldElement]:
""" """
Return ``x`` to power of [0, n-1]. Return ``x`` to power of [0, n-1], if n > 0. When n==0, an empty array is returned.
""" """
current_power = 1 current_power = 1
powers = [] powers = []
@ -256,6 +284,7 @@ def compute_powers(x: BLSFieldElement, n: uint64) -> Sequence[BLSFieldElement]:
return powers return powers
``` ```
### Polynomials ### Polynomials
#### `evaluate_polynomial_in_evaluation_form` #### `evaluate_polynomial_in_evaluation_form`
@ -270,7 +299,7 @@ def evaluate_polynomial_in_evaluation_form(polynomial: Polynomial,
""" """
width = len(polynomial) width = len(polynomial)
assert width == FIELD_ELEMENTS_PER_BLOB assert width == FIELD_ELEMENTS_PER_BLOB
inverse_width = bls_modular_inverse(width) inverse_width = bls_modular_inverse(BLSFieldElement(width))
# Make sure we won't divide by zero during division # Make sure we won't divide by zero during division
assert z not in ROOTS_OF_UNITY assert z not in ROOTS_OF_UNITY
@ -279,9 +308,11 @@ def evaluate_polynomial_in_evaluation_form(polynomial: Polynomial,
result = 0 result = 0
for i in range(width): for i in range(width):
result += div(int(polynomial[i]) * int(roots_of_unity_brp[i]), (int(z) - int(roots_of_unity_brp[i]))) a = BLSFieldElement(int(polynomial[i]) * int(roots_of_unity_brp[i]) % BLS_MODULUS)
result = result * (pow(z, width, BLS_MODULUS) - 1) * inverse_width % BLS_MODULUS b = BLSFieldElement((int(BLS_MODULUS) + int(z) - int(roots_of_unity_brp[i])) % BLS_MODULUS)
return result result += int(div(a, b) % BLS_MODULUS)
result = result * int(pow(z, width, BLS_MODULUS) - 1) * int(inverse_width)
return BLSFieldElement(result % BLS_MODULUS)
``` ```
### KZG ### KZG
@ -341,17 +372,13 @@ def compute_kzg_proof(polynomial: Polynomial, z: BLSFieldElement) -> KZGProof:
Compute KZG proof at point `z` with `polynomial` being in evaluation form Compute KZG proof at point `z` with `polynomial` being in evaluation form
Do this by computing the quotient polynomial in evaluation form: q(x) = (p(x) - p(z)) / (x - z) Do this by computing the quotient polynomial in evaluation form: q(x) = (p(x) - p(z)) / (x - z)
""" """
# To avoid SSZ overflow/underflow, convert element into int
polynomial = [int(i) for i in polynomial]
z = int(z)
y = evaluate_polynomial_in_evaluation_form(polynomial, z) y = evaluate_polynomial_in_evaluation_form(polynomial, z)
polynomial_shifted = [(p - int(y)) % BLS_MODULUS for p in polynomial] polynomial_shifted = [BLSFieldElement((int(p) - int(y)) % BLS_MODULUS) for p in polynomial]
# Make sure we won't divide by zero during division # Make sure we won't divide by zero during division
assert z not in ROOTS_OF_UNITY assert z not in ROOTS_OF_UNITY
denominator_poly = [(int(x) - z) % BLS_MODULUS for x in bit_reversal_permutation(ROOTS_OF_UNITY)] denominator_poly = [BLSFieldElement((int(x) - int(z)) % BLS_MODULUS)
for x in bit_reversal_permutation(ROOTS_OF_UNITY)]
# Calculate quotient polynomial by doing point-by-point division # Calculate quotient polynomial by doing point-by-point division
quotient_polynomial = [div(a, b) for a, b in zip(polynomial_shifted, denominator_poly)] quotient_polynomial = [div(a, b) for a, b in zip(polynomial_shifted, denominator_poly)]
@ -367,17 +394,18 @@ def compute_aggregated_poly_and_commitment(
""" """
Return (1) the aggregated polynomial, (2) the aggregated KZG commitment, Return (1) the aggregated polynomial, (2) the aggregated KZG commitment,
and (3) the polynomial evaluation random challenge. and (3) the polynomial evaluation random challenge.
This function should also work with blobs == [] and kzg_commitments == []
""" """
assert len(blobs) == len(kzg_commitments)
# Convert blobs to polynomials # Convert blobs to polynomials
polynomials = [blob_to_polynomial(blob) for blob in blobs] polynomials = [blob_to_polynomial(blob) for blob in blobs]
# Generate random linear combination challenges # Generate random linear combination and evaluation challenges
r = hash_to_bls_field(polynomials, kzg_commitments) r_powers, evaluation_challenge = compute_challenges(polynomials, kzg_commitments)
r_powers = compute_powers(r, len(kzg_commitments))
evaluation_challenge = int(r_powers[-1]) * r % BLS_MODULUS
# Create aggregated polynomial in evaluation form # Create aggregated polynomial in evaluation form
aggregated_poly = Polynomial(poly_lincomb(polynomials, r_powers)) aggregated_poly = poly_lincomb(polynomials, r_powers)
# Compute commitment to aggregated polynomial # Compute commitment to aggregated polynomial
aggregated_poly_commitment = KZGCommitment(g1_lincomb(kzg_commitments, r_powers)) aggregated_poly_commitment = KZGCommitment(g1_lincomb(kzg_commitments, r_powers))
@ -390,6 +418,7 @@ def compute_aggregated_poly_and_commitment(
```python ```python
def compute_aggregate_kzg_proof(blobs: Sequence[Blob]) -> KZGProof: def compute_aggregate_kzg_proof(blobs: Sequence[Blob]) -> KZGProof:
""" """
Given a list of blobs, return the aggregated KZG proof that is used to verify them against their commitments.
Public method. Public method.
""" """
commitments = [blob_to_kzg_commitment(blob) for blob in blobs] commitments = [blob_to_kzg_commitment(blob) for blob in blobs]
@ -405,8 +434,10 @@ def compute_aggregate_kzg_proof(blobs: Sequence[Blob]) -> KZGProof:
```python ```python
def verify_aggregate_kzg_proof(blobs: Sequence[Blob], def verify_aggregate_kzg_proof(blobs: Sequence[Blob],
expected_kzg_commitments: Sequence[KZGCommitment], expected_kzg_commitments: Sequence[KZGCommitment],
kzg_aggregated_proof: KZGCommitment) -> bool: kzg_aggregated_proof: KZGProof) -> bool:
""" """
Given a list of blobs and an aggregated KZG proof, verify that they correspond to the provided commitments.
Public method. Public method.
""" """
aggregated_poly, aggregated_poly_commitment, evaluation_challenge = compute_aggregated_poly_and_commitment( aggregated_poly, aggregated_poly_commitment, evaluation_challenge = compute_aggregated_poly_and_commitment(

View File

@ -46,6 +46,7 @@ Implementers may also retrieve blobs individually per transaction.
```python ```python
def get_blobs_and_kzg_commitments(payload_id: PayloadId) -> Tuple[Sequence[BLSFieldElement], Sequence[KZGCommitment]]: def get_blobs_and_kzg_commitments(payload_id: PayloadId) -> Tuple[Sequence[BLSFieldElement], Sequence[KZGCommitment]]:
# pylint: disable=unused-argument
... ...
``` ```

View File

@ -745,6 +745,8 @@ For example, if slot 4 were empty in the previous example, the returned array wo
`step` is deprecated and must be set to 1. Clients may respond with a single block if a larger step is returned during the deprecation transition period. `step` is deprecated and must be set to 1. Clients may respond with a single block if a larger step is returned during the deprecation transition period.
`/eth2/beacon_chain/req/beacon_blocks_by_range/1/` is deprecated. Clients MAY respond with an empty list during the deprecation transition period.
`BeaconBlocksByRange` is primarily used to sync historical blocks. `BeaconBlocksByRange` is primarily used to sync historical blocks.
The request MUST be encoded as an SSZ-container. The request MUST be encoded as an SSZ-container.
@ -831,6 +833,8 @@ Clients MUST support requesting blocks since the latest finalized epoch.
Clients MUST respond with at least one block, if they have it. Clients MUST respond with at least one block, if they have it.
Clients MAY limit the number of blocks in the response. Clients MAY limit the number of blocks in the response.
`/eth2/beacon_chain/req/beacon_blocks_by_root/1/` is deprecated. Clients MAY respond with an empty list during the deprecation transition period.
#### Ping #### Ping
**Protocol ID:** `/eth2/beacon_chain/req/ping/1/` **Protocol ID:** `/eth2/beacon_chain/req/ping/1/`

View File

@ -25,6 +25,12 @@ def _run_validate_blobs_sidecar_test(spec, state, blob_count):
spec.validate_blobs_sidecar(block.slot, block.hash_tree_root(), expected_commitments, blobs_sidecar) spec.validate_blobs_sidecar(block.slot, block.hash_tree_root(), expected_commitments, blobs_sidecar)
@with_eip4844_and_later
@spec_state_test
def test_validate_blobs_sidecar_zero_blobs(spec, state):
_run_validate_blobs_sidecar_test(spec, state, blob_count=0)
@with_eip4844_and_later @with_eip4844_and_later
@spec_state_test @spec_state_test
def test_validate_blobs_sidecar_one_blob(spec, state): def test_validate_blobs_sidecar_one_blob(spec, state):

View File

@ -165,6 +165,9 @@ def do_fork(state, spec, post_spec, fork_epoch, with_block=True, operation_dict=
elif post_spec.fork == CAPELLA: elif post_spec.fork == CAPELLA:
assert state.fork.previous_version == post_spec.config.BELLATRIX_FORK_VERSION assert state.fork.previous_version == post_spec.config.BELLATRIX_FORK_VERSION
assert state.fork.current_version == post_spec.config.CAPELLA_FORK_VERSION assert state.fork.current_version == post_spec.config.CAPELLA_FORK_VERSION
elif post_spec.fork == EIP4844:
assert state.fork.previous_version == post_spec.config.CAPELLA_FORK_VERSION
assert state.fork.current_version == post_spec.config.EIP4844_FORK_VERSION
if with_block: if with_block:
return state, _state_transition_and_sign_block_at_slot(post_spec, state, operation_dict=operation_dict) return state, _state_transition_and_sign_block_at_slot(post_spec, state, operation_dict=operation_dict)

View File

@ -60,7 +60,7 @@ def create_genesis_state(spec, validator_balances, activation_threshold):
previous_version = spec.config.BELLATRIX_FORK_VERSION previous_version = spec.config.BELLATRIX_FORK_VERSION
current_version = spec.config.CAPELLA_FORK_VERSION current_version = spec.config.CAPELLA_FORK_VERSION
elif spec.fork == EIP4844: elif spec.fork == EIP4844:
previous_version = spec.config.BELLATRIX_FORK_VERSION previous_version = spec.config.CAPELLA_FORK_VERSION
current_version = spec.config.EIP4844_FORK_VERSION current_version = spec.config.EIP4844_FORK_VERSION
state = spec.BeaconState( state = spec.BeaconState(