diff --git a/README.md b/README.md index f3647eee04..aa22e05c5a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Join the chat at https://discord.gg/qGpsxSA](https://img.shields.io/badge/chat-on%20discord-blue.svg)](https://discord.gg/qGpsxSA) [![Join the chat at https://gitter.im/ethereum/sharding](https://badges.gitter.im/ethereum/sharding.svg)](https://gitter.im/ethereum/sharding?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -To learn more about proof-of-stake and sharding, see the [PoS FAQ](https://eth.wiki/en/concepts/proof-of-stake-faqs), [sharding FAQ](https://eth.wiki/sharding/Sharding-FAQs) and the [research compendium](https://notes.ethereum.org/s/H1PGqDhpm). +To learn more about proof-of-stake and sharding, see the [PoS documentation](https://ethereum.org/en/developers/docs/consensus-mechanisms/pos/), [sharding documentation](https://ethereum.org/en/upgrades/sharding/) and the [research compendium](https://notes.ethereum.org/s/H1PGqDhpm). This repository hosts the current Ethereum proof-of-stake specifications. Discussions about design rationale and proposed changes can be brought up and discussed as issues. Solidified, agreed-upon changes to the spec can be made through pull requests. diff --git a/setup.py b/setup.py index 6db4aa8707..ec62706aa0 100644 --- a/setup.py +++ b/setup.py @@ -232,7 +232,7 @@ def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str]) -> if not _is_constant_id(name): # Check for short type declarations - if value.startswith(("uint", "Bytes", "ByteList", "Union", "Vector", "List")): + if value.startswith(("uint", "Bytes", "ByteList", "Union", "Vector", "List", "ByteVector")): custom_types[name] = value continue @@ -590,7 +590,6 @@ def imports(cls, preset_name: str): return super().imports(preset_name) + f''' from eth2spec.utils import kzg from eth2spec.bellatrix import {preset_name} as bellatrix -from eth2spec.utils.ssz.ssz_impl import serialize as ssz_serialize ''' @@ -617,12 +616,13 @@ def sundry_functions(cls) -> str: ROOTS_OF_UNITY = kzg.compute_roots_of_unity(TESTING_FIELD_ELEMENTS_PER_BLOB) -def retrieve_blobs_sidecar(slot: Slot, beacon_block_root: Root) -> BlobsSidecar: - pass''' +def retrieve_blobs_sidecar(slot: Slot, beacon_block_root: Root) -> Optional[BlobsSidecar]: + return "TEST"''' @classmethod def hardcoded_custom_type_dep_constants(cls, spec_object) -> str: constants = { + 'BYTES_PER_FIELD_ELEMENT': spec_object.constant_vars['BYTES_PER_FIELD_ELEMENT'].value, 'FIELD_ELEMENTS_PER_BLOB': spec_object.preset_vars['FIELD_ELEMENTS_PER_BLOB'].value, 'MAX_BLOBS_PER_BLOCK': spec_object.preset_vars['MAX_BLOBS_PER_BLOCK'].value, } @@ -1131,7 +1131,7 @@ def run(self): "pycryptodome==3.15.0", "py_ecc==6.0.0", "milagro_bls_binding==1.9.0", - "remerkleable==0.1.24", + "remerkleable==0.1.25", RUAMEL_YAML_VERSION, "lru-dict==1.1.8", MARKO_VERSION, diff --git a/specs/altair/light-client/full-node.md b/specs/altair/light-client/full-node.md index 09810a0373..e3f4d7a3e3 100644 --- a/specs/altair/light-client/full-node.md +++ b/specs/altair/light-client/full-node.md @@ -84,13 +84,13 @@ def create_light_client_update(state: BeaconState, header = state.latest_block_header.copy() header.state_root = hash_tree_root(state) assert hash_tree_root(header) == hash_tree_root(block.message) - update_signature_period = compute_sync_committee_period(compute_epoch_at_slot(block.message.slot)) + update_signature_period = compute_sync_committee_period_at_slot(block.message.slot) assert attested_state.slot == attested_state.latest_block_header.slot attested_header = attested_state.latest_block_header.copy() attested_header.state_root = hash_tree_root(attested_state) assert hash_tree_root(attested_header) == block.message.parent_root - update_attested_period = compute_sync_committee_period(compute_epoch_at_slot(attested_header.slot)) + update_attested_period = compute_sync_committee_period_at_slot(attested_header.slot) # `next_sync_committee` is only useful if the message is signed by the current sync committee if update_attested_period == update_signature_period: @@ -133,7 +133,7 @@ def create_light_client_update(state: BeaconState, Full nodes SHOULD provide the best derivable `LightClientUpdate` (according to `is_better_update`) for each sync committee period covering any epochs in range `[max(ALTAIR_FORK_EPOCH, current_epoch - MIN_EPOCHS_FOR_BLOCK_REQUESTS), current_epoch]` where `current_epoch` is defined by the current wall-clock time. Full nodes MAY also provide `LightClientUpdate` for other sync committee periods. - `LightClientUpdate` are assigned to sync committee periods based on their `attested_header.slot` -- `LightClientUpdate` are only considered if `compute_sync_committee_period(compute_epoch_at_slot(update.attested_header.slot)) == compute_sync_committee_period(compute_epoch_at_slot(update.signature_slot))` +- `LightClientUpdate` are only considered if `compute_sync_committee_period_at_slot(update.attested_header.slot) == compute_sync_committee_period_at_slot(update.signature_slot)` - Only `LightClientUpdate` with `next_sync_committee` as selected by fork choice are provided, regardless of ranking by `is_better_update`. To uniquely identify a non-finalized sync committee fork, all of `period`, `current_sync_committee` and `next_sync_committee` need to be incorporated, as sync committees may reappear over time. ### `create_light_client_finality_update` diff --git a/specs/altair/light-client/p2p-interface.md b/specs/altair/light-client/p2p-interface.md index 5c2b27b22d..f7575cf534 100644 --- a/specs/altair/light-client/p2p-interface.md +++ b/specs/altair/light-client/p2p-interface.md @@ -71,6 +71,17 @@ For light clients, the following validations MUST additionally pass before forwa Light clients SHOULD call `process_light_client_finality_update` even if the message is ignored. +The gossip `ForkDigest`-context is determined based on `compute_fork_version(compute_epoch_at_slot(finality_update.attested_header.slot))`. + +Per `context = compute_fork_digest(fork_version, genesis_validators_root)`: + +[0]: # (eth2spec: skip) + +| `fork_version` | Message SSZ type | +| ------------------------------- | ------------------------------------ | +| `GENESIS_FORK_VERSION` | n/a | +| `ALTAIR_FORK_VERSION` and later | `altair.LightClientFinalityUpdate` | + ###### `light_client_optimistic_update` This topic is used to propagate the latest `LightClientOptimisticUpdate` to light clients, allowing them to keep track of the latest `optimistic_header`. @@ -88,6 +99,17 @@ For light clients, the following validations MUST additionally pass before forwa Light clients SHOULD call `process_light_client_optimistic_update` even if the message is ignored. +The gossip `ForkDigest`-context is determined based on `compute_fork_version(compute_epoch_at_slot(optimistic_update.attested_header.slot))`. + +Per `context = compute_fork_digest(fork_version, genesis_validators_root)`: + +[0]: # (eth2spec: skip) + +| `fork_version` | Message SSZ type | +| ------------------------------- | ------------------------------------ | +| `GENESIS_FORK_VERSION` | n/a | +| `ALTAIR_FORK_VERSION` and later | `altair.LightClientOptimisticUpdate` | + ### The Req/Resp domain #### Messages diff --git a/specs/altair/light-client/sync-protocol.md b/specs/altair/light-client/sync-protocol.md index ede86f852e..793483bc0c 100644 --- a/specs/altair/light-client/sync-protocol.md +++ b/specs/altair/light-client/sync-protocol.md @@ -77,9 +77,9 @@ Additional documents describe how the light client sync protocol can be used: ```python class LightClientBootstrap(Container): - # The requested beacon block header + # Header matching the requested beacon block root header: BeaconBlockHeader - # Current sync committee corresponding to `header` + # Current sync committee corresponding to `header.state_root` current_sync_committee: SyncCommittee current_sync_committee_branch: Vector[Bytes32, floorlog2(CURRENT_SYNC_COMMITTEE_INDEX)] ``` @@ -88,12 +88,12 @@ class LightClientBootstrap(Container): ```python class LightClientUpdate(Container): - # The beacon block header that is attested to by the sync committee + # Header attested to by the sync committee attested_header: BeaconBlockHeader - # Next sync committee corresponding to `attested_header` + # Next sync committee corresponding to `attested_header.state_root` next_sync_committee: SyncCommittee next_sync_committee_branch: Vector[Bytes32, floorlog2(NEXT_SYNC_COMMITTEE_INDEX)] - # The finalized beacon block header attested to by Merkle branch + # Finalized header corresponding to `attested_header.state_root` finalized_header: BeaconBlockHeader finality_branch: Vector[Bytes32, floorlog2(FINALIZED_ROOT_INDEX)] # Sync committee aggregate signature @@ -106,9 +106,9 @@ class LightClientUpdate(Container): ```python class LightClientFinalityUpdate(Container): - # The beacon block header that is attested to by the sync committee + # Header attested to by the sync committee attested_header: BeaconBlockHeader - # The finalized beacon block header attested to by Merkle branch + # Finalized header corresponding to `attested_header.state_root` finalized_header: BeaconBlockHeader finality_branch: Vector[Bytes32, floorlog2(FINALIZED_ROOT_INDEX)] # Sync committee aggregate signature @@ -121,7 +121,7 @@ class LightClientFinalityUpdate(Container): ```python class LightClientOptimisticUpdate(Container): - # The beacon block header that is attested to by the sync committee + # Header attested to by the sync committee attested_header: BeaconBlockHeader # Sync committee aggregate signature sync_aggregate: SyncAggregate @@ -134,9 +134,9 @@ class LightClientOptimisticUpdate(Container): ```python @dataclass class LightClientStore(object): - # Beacon block header that is finalized + # Header that is finalized finalized_header: BeaconBlockHeader - # Sync committees corresponding to the header + # Sync committees corresponding to the finalized header current_sync_committee: SyncCommittee next_sync_committee: SyncCommittee # Best available header to switch finalized head to if we see nothing else diff --git a/specs/bellatrix/p2p-interface.md b/specs/bellatrix/p2p-interface.md index 02d4ef6c9f..4d4044689b 100644 --- a/specs/bellatrix/p2p-interface.md +++ b/specs/bellatrix/p2p-interface.md @@ -110,7 +110,7 @@ The following gossip validation from prior specifications MUST NOT be applied if ### Transitioning the gossip See gossip transition details found in the [Altair document](../altair/p2p-interface.md#transitioning-the-gossip) for -details on how to handle transitioning gossip topics for EIP-4844. +details on how to handle transitioning gossip topics. ## The Req/Resp domain diff --git a/specs/capella/p2p-interface.md b/specs/capella/p2p-interface.md index e69de29bb2..4cb23a67e1 100644 --- a/specs/capella/p2p-interface.md +++ b/specs/capella/p2p-interface.md @@ -0,0 +1,110 @@ +# Capella -- Networking + +This document contains the networking specification for Capella. + +The specification of these changes continues in the same format as the network specifications of previous upgrades, and assumes them as pre-requisite. + +## Table of contents + + + + + +- [Modifications in Capella](#modifications-in-capella) + - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) + - [Topics and messages](#topics-and-messages) + - [Global topics](#global-topics) + - [`beacon_block`](#beacon_block) + - [`bls_to_execution_change`](#bls_to_execution_change) + - [Transitioning the gossip](#transitioning-the-gossip) + - [The Req/Resp domain](#the-reqresp-domain) + - [Messages](#messages) + - [BeaconBlocksByRange v2](#beaconblocksbyrange-v2) + - [BeaconBlocksByRoot v2](#beaconblocksbyroot-v2) + + + + + +# Modifications in Capella + +## The gossip domain: gossipsub + +A new topic is added to support the gossip of withdrawal credential change messages. And an existing topic is upgraded for updated types in Capella. + +### Topics and messages + +Topics follow the same specification as in prior upgrades. All existing topics remain stable except the beacon block topic which is updated with the modified type. + +The new topics along with the type of the `data` field of a gossipsub message are given in this table: + +| Name | Message Type | +| - | - | +| `beacon_block` | `SignedBeaconBlock` (modified) | +| `bls_to_execution_change` | `SignedBLSToExecutionChange` | + +Note that the `ForkDigestValue` path segment of the topic separates the old and the new `beacon_block` topics. + +#### Global topics + +Capella changes the type of the global beacon block topic and adds one global topic to propagate withdrawal credential change messages to all potential proposers of beacon blocks. + +##### `beacon_block` + +The *type* of the payload of this topic changes to the (modified) `SignedBeaconBlock` found in Capella. +Specifically, this type changes with the addition of `bls_to_execution_changes` to the inner `BeaconBlockBody`. +See Capella [state transition document](./beacon-chain.md#beaconblockbody) for further details. + +##### `bls_to_execution_change` + +This topic is used to propagate signed bls to execution change messages to be included in future blocks. + +The following validations MUST pass before forwarding the `signed_bls_to_execution_change` on the network: + +- _[IGNORE]_ The `signed_bls_to_execution_change` is the first valid signed bls to execution change received + for the validator with index `signed_bls_to_execution_change.message.validator_index`. +- _[REJECT]_ All of the conditions within `process_bls_to_execution_change` pass validation. + +### Transitioning the gossip + +See gossip transition details found in the [Altair document](../altair/p2p-interface.md#transitioning-the-gossip) for +details on how to handle transitioning gossip topics for Capella. + +## The Req/Resp domain + +### Messages + +#### BeaconBlocksByRange v2 + +**Protocol ID:** `/eth2/beacon_chain/req/beacon_blocks_by_range/2/` + +The Capella fork-digest is introduced to the `context` enum to specify Capella block type. + +Per `context = compute_fork_digest(fork_version, genesis_validators_root)`: + +[0]: # (eth2spec: skip) + +| `fork_version` | Chunk SSZ type | +| ------------------------ | -------------------------- | +| `GENESIS_FORK_VERSION` | `phase0.SignedBeaconBlock` | +| `ALTAIR_FORK_VERSION` | `altair.SignedBeaconBlock` | +| `BELLATRIX_FORK_VERSION` | `bellatrix.SignedBeaconBlock` | +| `CAPELLA_FORK_VERSION` | `capella.SignedBeaconBlock` | + +#### BeaconBlocksByRoot v2 + +**Protocol ID:** `/eth2/beacon_chain/req/beacon_blocks_by_root/2/` + +The Capella fork-digest is introduced to the `context` enum to specify Capella block type. + +Per `context = compute_fork_digest(fork_version, genesis_validators_root)`: + +[1]: # (eth2spec: skip) + +| `fork_version` | Chunk SSZ type | +| ------------------------ | -------------------------- | +| `GENESIS_FORK_VERSION` | `phase0.SignedBeaconBlock` | +| `ALTAIR_FORK_VERSION` | `altair.SignedBeaconBlock` | +| `BELLATRIX_FORK_VERSION` | `bellatrix.SignedBeaconBlock` | +| `CAPELLA_FORK_VERSION` | `capella.SignedBeaconBlock` | + diff --git a/specs/capella/validator.md b/specs/capella/validator.md index 85dbd7e00b..90176e035e 100644 --- a/specs/capella/validator.md +++ b/specs/capella/validator.md @@ -18,6 +18,7 @@ - [Block proposal](#block-proposal) - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) - [ExecutionPayload](#executionpayload) + - [BLS to execution changes](#bls-to-execution-changes) @@ -106,3 +107,7 @@ def prepare_execution_payload(state: BeaconState, payload_attributes=payload_attributes, ) ``` + +##### BLS to execution changes + +Up to `MAX_BLS_TO_EXECUTION_CHANGES`, [`BLSToExecutionChange`](./beacon-chain.md#blstoexecutionchange) objects can be included in the `block`. The BLS to execution changes must satisfy the verification conditions found in [BLS to execution change processing](./beacon-chain.md#new-process_bls_to_execution_change). diff --git a/specs/eip4844/beacon-chain.md b/specs/eip4844/beacon-chain.md index 4cf9535933..a5f9124724 100644 --- a/specs/eip4844/beacon-chain.md +++ b/specs/eip4844/beacon-chain.md @@ -23,6 +23,8 @@ - [`ExecutionPayloadHeader`](#executionpayloadheader) - [Helper functions](#helper-functions) - [Misc](#misc) + - [`validate_blobs_sidecar`](#validate_blobs_sidecar) + - [`is_data_available`](#is_data_available) - [`kzg_commitment_to_versioned_hash`](#kzg_commitment_to_versioned_hash) - [`tx_peek_blob_versioned_hashes`](#tx_peek_blob_versioned_hashes) - [`verify_kzg_commitments_against_transactions`](#verify_kzg_commitments_against_transactions) @@ -44,9 +46,7 @@ This upgrade adds blobs to the beacon chain as part of EIP-4844. | Name | SSZ equivalent | Description | | - | - | - | -| `Blob` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | | | `VersionedHash` | `Bytes32` | | -| `KZGCommitment` | `Bytes48` | Same as BLS standard "is valid pubkey" check but also allows `0x00..00` for point-at-infinity | ## Constants @@ -55,7 +55,6 @@ This upgrade adds blobs to the beacon chain as part of EIP-4844. | Name | Value | | - | - | | `BLOB_TX_TYPE` | `uint8(0x05)` | -| `FIELD_ELEMENTS_PER_BLOB` | `uint64(4096)` | | `VERSIONED_HASH_VERSION_KZG` | `Bytes1(0x01)` | ### Domain types @@ -150,6 +149,43 @@ class ExecutionPayloadHeader(Container): ### Misc +#### `validate_blobs_sidecar` + +```python +def validate_blobs_sidecar(slot: Slot, + beacon_block_root: Root, + expected_kzg_commitments: Sequence[KZGCommitment], + blobs_sidecar: BlobsSidecar) -> None: + assert slot == blobs_sidecar.beacon_block_slot + assert beacon_block_root == blobs_sidecar.beacon_block_root + blobs = blobs_sidecar.blobs + kzg_aggregated_proof = blobs_sidecar.kzg_aggregated_proof + assert len(expected_kzg_commitments) == len(blobs) + + assert verify_aggregate_kzg_proof(blobs, expected_kzg_commitments, kzg_aggregated_proof) +``` + +#### `is_data_available` + +The implementation of `is_data_available` is meant to change with later sharding upgrades. +Initially, it requires every verifying actor to retrieve the matching `BlobsSidecar`, +and validate the sidecar with `validate_blobs_sidecar`. + +Without the sidecar the block may be processed further optimistically, +but MUST NOT be considered valid until a valid `BlobsSidecar` has been downloaded. + +```python +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. + 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) + + return True +``` + + #### `kzg_commitment_to_versioned_hash` ```python @@ -204,6 +240,9 @@ def process_block(state: BeaconState, block: BeaconBlock) -> None: process_operations(state, block.body) process_sync_aggregate(state, block.body.sync_aggregate) process_blob_kzg_commitments(state, block.body) # [New in EIP-4844] + + # New in EIP-4844, note: Can sync optimistically without this condition, see note on `is_data_available` + assert is_data_available(block.slot, hash_tree_root(block), block.body.blob_kzg_commitments) ``` #### Execution payload diff --git a/specs/eip4844/p2p-interface.md b/specs/eip4844/p2p-interface.md index 0563ff7e65..1576fe96f7 100644 --- a/specs/eip4844/p2p-interface.md +++ b/specs/eip4844/p2p-interface.md @@ -13,7 +13,6 @@ The specification of these changes continues in the same format as the network s - [Configuration](#configuration) - [Containers](#containers) - [`BlobsSidecar`](#blobssidecar) - - [`SignedBlobsSidecar`](#signedblobssidecar) - [`SignedBeaconBlockAndBlobsSidecar`](#signedbeaconblockandblobssidecar) - [The gossip domain: gossipsub](#the-gossip-domain-gossipsub) - [Topics and messages](#topics-and-messages) @@ -50,14 +49,6 @@ class BlobsSidecar(Container): kzg_aggregated_proof: KZGProof ``` -### `SignedBlobsSidecar` - -```python -class SignedBlobsSidecar(Container): - message: BlobsSidecar - signature: BLSSignature -``` - ### `SignedBeaconBlockAndBlobsSidecar` ```python diff --git a/specs/eip4844/polynomial-commitments.md b/specs/eip4844/polynomial-commitments.md index 2b25763466..c90c0d38d4 100644 --- a/specs/eip4844/polynomial-commitments.md +++ b/specs/eip4844/polynomial-commitments.md @@ -10,6 +10,8 @@ - [Custom types](#custom-types) - [Constants](#constants) - [Preset](#preset) + - [Blob](#blob) + - [Crypto](#crypto) - [Trusted setup](#trusted-setup) - [Helper functions](#helper-functions) - [Bit-reversal permutation](#bit-reversal-permutation) @@ -18,16 +20,22 @@ - [`bit_reversal_permutation`](#bit_reversal_permutation) - [BLS12-381 helpers](#bls12-381-helpers) - [`bytes_to_bls_field`](#bytes_to_bls_field) + - [`blob_to_polynomial`](#blob_to_polynomial) + - [`hash_to_bls_field`](#hash_to_bls_field) - [`bls_modular_inverse`](#bls_modular_inverse) - [`div`](#div) - [`g1_lincomb`](#g1_lincomb) - - [`vector_lincomb`](#vector_lincomb) + - [`poly_lincomb`](#poly_lincomb) + - [`compute_powers`](#compute_powers) + - [Polynomials](#polynomials) + - [`evaluate_polynomial_in_evaluation_form`](#evaluate_polynomial_in_evaluation_form) - [KZG](#kzg) - [`blob_to_kzg_commitment`](#blob_to_kzg_commitment) - [`verify_kzg_proof`](#verify_kzg_proof) - [`compute_kzg_proof`](#compute_kzg_proof) - - [Polynomials](#polynomials) - - [`evaluate_polynomial_in_evaluation_form`](#evaluate_polynomial_in_evaluation_form) + - [`compute_aggregated_poly_and_commitment`](#compute_aggregated_poly_and_commitment) + - [`compute_aggregate_kzg_proof`](#compute_aggregate_kzg_proof) + - [`verify_aggregate_kzg_proof`](#verify_aggregate_kzg_proof) @@ -46,16 +54,31 @@ This document specifies basic polynomial operations and KZG polynomial commitmen | `BLSFieldElement` | `uint256` | `x < BLS_MODULUS` | | `KZGCommitment` | `Bytes48` | Same as BLS standard "is valid pubkey" check but also allows `0x00..00` for point-at-infinity | | `KZGProof` | `Bytes48` | Same as for `KZGCommitment` | +| `Polynomial` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | a polynomial in evaluation form | +| `Blob` | `ByteVector[BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB]` | a basic blob data | ## Constants | Name | Value | Notes | | - | - | - | | `BLS_MODULUS` | `52435875175126190479447740508185965837690552500527637822603658699938581184513` | Scalar field modulus of BLS12-381 | -| `ROOTS_OF_UNITY` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | Roots of unity of order FIELD_ELEMENTS_PER_BLOB over the BLS12-381 field | +| `BYTES_PER_FIELD_ELEMENT` | `uint64(32)` | Bytes used to encode a BLS scalar field element | ## Preset +### Blob + +| Name | Value | +| - | - | +| `FIELD_ELEMENTS_PER_BLOB` | `uint64(4096)` | +| `FIAT_SHAMIR_PROTOCOL_DOMAIN` | `b'FSBLOBVERIFY_V1_'` | + +### Crypto + +| Name | Value | Notes | +| - | - | - | +| `ROOTS_OF_UNITY` | `Vector[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | Roots of unity of order FIELD_ELEMENTS_PER_BLOB over the BLS12-381 field | + ### Trusted setup The trusted setup is part of the preset: during testing a `minimal` insecure variant may be used, @@ -91,7 +114,7 @@ def is_power_of_two(value: int) -> bool: ```python def reverse_bits(n: int, order: int) -> int: """ - Reverse the bit order of an integer n + Reverse the bit order of an integer ``n``. """ assert is_power_of_two(order) # Convert n to binary with the same number of bits as "order" - 1, then reverse its bit order @@ -117,9 +140,51 @@ def bit_reversal_permutation(sequence: Sequence[T]) -> Sequence[T]: ```python def bytes_to_bls_field(b: Bytes32) -> BLSFieldElement: """ - Convert bytes to a BLS field scalar. The output is not uniform over the BLS field. + Convert 32-byte value to a BLS field scalar. The output is not uniform over the BLS field. + """ + return int.from_bytes(b, ENDIANNESS) % BLS_MODULUS +``` + +#### `blob_to_polynomial` + +```python +def blob_to_polynomial(blob: Blob) -> Polynomial: + """ + Convert a blob to list of BLS field scalars. + """ + polynomial = Polynomial() + 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) + assert value < BLS_MODULUS + polynomial[i] = value + return polynomial +``` + +#### `hash_to_bls_field` + +```python +def hash_to_bls_field(polys: Sequence[Polynomial], + comms: Sequence[KZGCommitment]) -> BLSFieldElement: + """ + Compute 32-byte hash of serialized polynomials and commitments concatenated. + This hash is then converted to a BLS field element, where the result is not uniform over the BLS field. + Return the BLS field element. """ - return int.from_bytes(b, "little") % BLS_MODULUS + # Append the number of polynomials and the degree of each polynomial as a domain separator + num_polys = int.to_bytes(len(polys), 8, ENDIANNESS) + degree_poly = int.to_bytes(FIELD_ELEMENTS_PER_BLOB, 8, ENDIANNESS) + data = FIAT_SHAMIR_PROTOCOL_DOMAIN + degree_poly + num_polys + + # Append each polynomial which is composed by field elements + for poly in polys: + for field_element in poly: + data += int.to_bytes(field_element, BYTES_PER_FIELD_ELEMENT, ENDIANNESS) + + # Append serialized G1 points + for commitment in comms: + data += commitment + + return bytes_to_bls_field(hash(data)) ``` #### `bls_modular_inverse` @@ -137,7 +202,9 @@ def bls_modular_inverse(x: BLSFieldElement) -> BLSFieldElement: ```python 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 ``` @@ -155,22 +222,65 @@ def g1_lincomb(points: Sequence[KZGCommitment], scalars: Sequence[BLSFieldElemen return KZGCommitment(bls.G1_to_bytes48(result)) ``` -#### `vector_lincomb` +#### `poly_lincomb` ```python -def vector_lincomb(vectors: Sequence[Sequence[BLSFieldElement]], - scalars: Sequence[BLSFieldElement]) -> Sequence[BLSFieldElement]: +def poly_lincomb(polys: Sequence[Polynomial], + scalars: Sequence[BLSFieldElement]) -> Polynomial: """ - Given a list of ``vectors``, interpret it as a 2D matrix and compute the linear combination - of each column with `scalars`: return the resulting vector. + Given a list of ``polynomials``, interpret it as a 2D matrix and compute the linear combination + of each column with `scalars`: return the resulting polynomials. """ - result = [0] * len(vectors[0]) - for v, s in zip(vectors, scalars): + result = [0] * len(polys[0]) + for v, s in zip(polys, scalars): for i, x in enumerate(v): result[i] = (result[i] + int(s) * int(x)) % BLS_MODULUS return [BLSFieldElement(x) for x in result] ``` +#### `compute_powers` + +```python +def compute_powers(x: BLSFieldElement, n: uint64) -> Sequence[BLSFieldElement]: + """ + Return ``x`` to power of [0, n-1]. + """ + current_power = 1 + powers = [] + for _ in range(n): + powers.append(BLSFieldElement(current_power)) + current_power = current_power * int(x) % BLS_MODULUS + return powers +``` + +### Polynomials + +#### `evaluate_polynomial_in_evaluation_form` + +```python +def evaluate_polynomial_in_evaluation_form(polynomial: Polynomial, + z: BLSFieldElement) -> BLSFieldElement: + """ + Evaluate a polynomial (in evaluation form) at an arbitrary point ``z``. + Uses the barycentric formula: + f(z) = (z**WIDTH - 1) / WIDTH * sum_(i=0)^WIDTH (f(DOMAIN[i]) * DOMAIN[i]) / (z - DOMAIN[i]) + """ + width = len(polynomial) + assert width == FIELD_ELEMENTS_PER_BLOB + inverse_width = bls_modular_inverse(width) + + # Make sure we won't divide by zero during division + assert z not in ROOTS_OF_UNITY + + roots_of_unity_brp = bit_reversal_permutation(ROOTS_OF_UNITY) + + result = 0 + for i in range(width): + result += div(int(polynomial[i]) * int(roots_of_unity_brp[i]), (int(z) - roots_of_unity_brp[i])) + result = result * (pow(z, width, BLS_MODULUS) - 1) * inverse_width % BLS_MODULUS + return result +``` + ### KZG KZG core functions. These are also defined in EIP-4844 execution specs. @@ -179,7 +289,7 @@ KZG core functions. These are also defined in EIP-4844 execution specs. ```python def blob_to_kzg_commitment(blob: Blob) -> KZGCommitment: - return g1_lincomb(bit_reversal_permutation(KZG_SETUP_LAGRANGE), blob) + return g1_lincomb(bit_reversal_permutation(KZG_SETUP_LAGRANGE), blob_to_polynomial(blob)) ``` #### `verify_kzg_proof` @@ -204,16 +314,16 @@ def verify_kzg_proof(polynomial_kzg: KZGCommitment, #### `compute_kzg_proof` ```python -def compute_kzg_proof(polynomial: Sequence[BLSFieldElement], z: BLSFieldElement) -> KZGProof: +def compute_kzg_proof(polynomial: Polynomial, z: BLSFieldElement) -> KZGProof: """ Compute KZG proof at point `z` with `polynomial` being in evaluation form + Do this by computing the quotient polynomial in evaluation form: q(x) = (p(x) - p(z)) / (x - z) """ # To avoid SSZ overflow/underflow, convert element into int polynomial = [int(i) for i in polynomial] z = int(z) - # Shift our polynomial first (in evaluation form we can't handle the division remainder) y = evaluate_polynomial_in_evaluation_form(polynomial, z) polynomial_shifted = [(p - int(y)) % BLS_MODULUS for p in polynomial] @@ -226,31 +336,59 @@ def compute_kzg_proof(polynomial: Sequence[BLSFieldElement], z: BLSFieldElement) return KZGProof(g1_lincomb(bit_reversal_permutation(KZG_SETUP_LAGRANGE), quotient_polynomial)) ``` -### Polynomials - -#### `evaluate_polynomial_in_evaluation_form` +#### `compute_aggregated_poly_and_commitment` ```python -def evaluate_polynomial_in_evaluation_form(polynomial: Sequence[BLSFieldElement], - z: BLSFieldElement) -> BLSFieldElement: +def compute_aggregated_poly_and_commitment( + blobs: Sequence[Blob], + kzg_commitments: Sequence[KZGCommitment]) -> Tuple[Polynomial, KZGCommitment, BLSFieldElement]: """ - Evaluate a polynomial (in evaluation form) at an arbitrary point `z` - Uses the barycentric formula: - f(z) = (1 - z**WIDTH) / WIDTH * sum_(i=0)^WIDTH (f(DOMAIN[i]) * DOMAIN[i]) / (z - DOMAIN[i]) + Return (1) the aggregated polynomial, (2) the aggregated KZG commitment, + and (3) the polynomial evaluation random challenge. """ - width = len(polynomial) - assert width == FIELD_ELEMENTS_PER_BLOB - inverse_width = bls_modular_inverse(width) + # Convert blobs to polynomials + polynomials = [blob_to_polynomial(blob) for blob in blobs] - # Make sure we won't divide by zero during division - assert z not in ROOTS_OF_UNITY + # Generate random linear combination challenges + r = hash_to_bls_field(polynomials, kzg_commitments) + r_powers = compute_powers(r, len(kzg_commitments)) + evaluation_challenge = int(r_powers[-1]) * r % BLS_MODULUS - roots_of_unity_brp = bit_reversal_permutation(ROOTS_OF_UNITY) + # Create aggregated polynomial in evaluation form + aggregated_poly = Polynomial(poly_lincomb(polynomials, r_powers)) - result = 0 - for i in range(width): - result += div(int(polynomial[i]) * int(roots_of_unity_brp[i]), (z - roots_of_unity_brp[i])) - result = result * (pow(z, width, BLS_MODULUS) - 1) * inverse_width % BLS_MODULUS - return result + # Compute commitment to aggregated polynomial + aggregated_poly_commitment = KZGCommitment(g1_lincomb(kzg_commitments, r_powers)) + + return aggregated_poly, aggregated_poly_commitment, evaluation_challenge +``` + +#### `compute_aggregate_kzg_proof` + +```python +def compute_aggregate_kzg_proof(blobs: Sequence[Blob]) -> KZGProof: + commitments = [blob_to_kzg_commitment(blob) for blob in blobs] + aggregated_poly, aggregated_poly_commitment, evaluation_challenge = compute_aggregated_poly_and_commitment( + blobs, + commitments + ) + return compute_kzg_proof(aggregated_poly, evaluation_challenge) ``` +#### `verify_aggregate_kzg_proof` + +```python +def verify_aggregate_kzg_proof(blobs: Sequence[Blob], + expected_kzg_commitments: Sequence[KZGCommitment], + kzg_aggregated_proof: KZGCommitment) -> bool: + aggregated_poly, aggregated_poly_commitment, evaluation_challenge = compute_aggregated_poly_and_commitment( + blobs, + expected_kzg_commitments, + ) + + # Evaluate aggregated polynomial at `evaluation_challenge` (evaluation function checks for div-by-zero) + y = evaluate_polynomial_in_evaluation_form(aggregated_poly, evaluation_challenge) + + # Verify aggregated proof + return verify_kzg_proof(aggregated_poly_commitment, evaluation_challenge, y, kzg_aggregated_proof) +``` diff --git a/specs/eip4844/validator.md b/specs/eip4844/validator.md index 0cd420637f..d03b1842bb 100644 --- a/specs/eip4844/validator.md +++ b/specs/eip4844/validator.md @@ -10,17 +10,7 @@ - [Introduction](#introduction) - [Prerequisites](#prerequisites) -- [Custom types](#custom-types) -- [Containers](#containers) - - [`BlobsAndCommitments`](#blobsandcommitments) - - [`PolynomialAndCommitment`](#polynomialandcommitment) - [Helpers](#helpers) - - [`is_data_available`](#is_data_available) - - [`hash_to_bls_field`](#hash_to_bls_field) - - [`compute_powers`](#compute_powers) - - [`compute_aggregated_poly_and_commitment`](#compute_aggregated_poly_and_commitment) - - [`validate_blobs_sidecar`](#validate_blobs_sidecar) - - [`compute_proof_from_blobs`](#compute_proof_from_blobs) - [`get_blobs_and_kzg_commitments`](#get_blobs_and_kzg_commitments) - [Beacon chain responsibilities](#beacon-chain-responsibilities) - [Block and sidecar proposal](#block-and-sidecar-proposal) @@ -45,140 +35,8 @@ All behaviors and definitions defined in this document, and documents it extends All terminology, constants, functions, and protocol mechanics defined in the updated [Beacon Chain doc of EIP4844](./beacon-chain.md) are requisite for this document and used throughout. Please see related Beacon Chain doc before continuing and use them as a reference throughout. -## Custom types - -| Name | SSZ equivalent | Description | -| - | - | - | -| `Polynomial` | `List[BLSFieldElement, FIELD_ELEMENTS_PER_BLOB]` | a polynomial in evaluation form | - -## Containers - -### `BlobsAndCommitments` - -```python -class BlobsAndCommitments(Container): - blobs: List[Blob, MAX_BLOBS_PER_BLOCK] - kzg_commitments: List[KZGCommitment, MAX_BLOBS_PER_BLOCK] -``` - -### `PolynomialAndCommitment` - -```python -class PolynomialAndCommitment(Container): - polynomial: Polynomial - kzg_commitment: KZGCommitment -``` - - ## Helpers -### `is_data_available` - -The implementation of `is_data_available` is meant to change with later sharding upgrades. -Initially, it requires every verifying actor to retrieve the matching `BlobsSidecar`, -and validate the sidecar with `validate_blobs_sidecar`. - -Without the sidecar the block may be processed further optimistically, -but MUST NOT be considered valid until a valid `BlobsSidecar` has been downloaded. - -```python -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. - sidecar = retrieve_blobs_sidecar(slot, beacon_block_root) - validate_blobs_sidecar(slot, beacon_block_root, blob_kzg_commitments, sidecar) - - return True -``` - -### `hash_to_bls_field` - -```python -def hash_to_bls_field(x: Container) -> BLSFieldElement: - """ - Compute 32-byte hash of serialized container and convert it to BLS field. - The output is not uniform over the BLS field. - """ - return bytes_to_bls_field(hash(ssz_serialize(x))) -``` - -### `compute_powers` -```python -def compute_powers(x: BLSFieldElement, n: uint64) -> Sequence[BLSFieldElement]: - """ - Return ``x`` to power of [0, n-1]. - """ - current_power = 1 - powers = [] - for _ in range(n): - powers.append(BLSFieldElement(current_power)) - current_power = current_power * int(x) % BLS_MODULUS - return powers -``` - -### `compute_aggregated_poly_and_commitment` - -```python -def compute_aggregated_poly_and_commitment( - blobs: Sequence[Blob], - kzg_commitments: Sequence[KZGCommitment]) -> Tuple[Polynomial, KZGCommitment]: - """ - Return the aggregated polynomial and aggregated KZG commitment. - """ - # Generate random linear combination challenges - r = hash_to_bls_field(BlobsAndCommitments(blobs=blobs, kzg_commitments=kzg_commitments)) - r_powers = compute_powers(r, len(kzg_commitments)) - - # Create aggregated polynomial in evaluation form - aggregated_poly = Polynomial(vector_lincomb(blobs, r_powers)) - - # Compute commitment to aggregated polynomial - aggregated_poly_commitment = KZGCommitment(g1_lincomb(kzg_commitments, r_powers)) - - return aggregated_poly, aggregated_poly_commitment -``` - -### `validate_blobs_sidecar` - -```python -def validate_blobs_sidecar(slot: Slot, - beacon_block_root: Root, - expected_kzg_commitments: Sequence[KZGCommitment], - blobs_sidecar: BlobsSidecar) -> None: - assert slot == blobs_sidecar.beacon_block_slot - assert beacon_block_root == blobs_sidecar.beacon_block_root - blobs = blobs_sidecar.blobs - kzg_aggregated_proof = blobs_sidecar.kzg_aggregated_proof - assert len(expected_kzg_commitments) == len(blobs) - - aggregated_poly, aggregated_poly_commitment = compute_aggregated_poly_and_commitment( - blobs, - expected_kzg_commitments, - ) - - # Generate challenge `x` and evaluate the aggregated polynomial at `x` - x = hash_to_bls_field( - PolynomialAndCommitment(polynomial=aggregated_poly, kzg_commitment=aggregated_poly_commitment) - ) - # Evaluate aggregated polynomial at `x` (evaluation function checks for div-by-zero) - y = evaluate_polynomial_in_evaluation_form(aggregated_poly, x) - - # Verify aggregated proof - assert verify_kzg_proof(aggregated_poly_commitment, x, y, kzg_aggregated_proof) -``` - -### `compute_proof_from_blobs` - -```python -def compute_proof_from_blobs(blobs: Sequence[Blob]) -> KZGProof: - commitments = [blob_to_kzg_commitment(blob) for blob in blobs] - aggregated_poly, aggregated_poly_commitment = compute_aggregated_poly_and_commitment(blobs, commitments) - x = hash_to_bls_field(PolynomialAndCommitment( - polynomial=aggregated_poly, - kzg_commitment=aggregated_poly_commitment, - )) - return compute_kzg_proof(aggregated_poly, x) -``` - ### `get_blobs_and_kzg_commitments` The interface to retrieve blobs and corresponding kzg commitments. @@ -236,7 +94,7 @@ def get_blobs_sidecar(block: BeaconBlock, blobs: Sequence[Blob]) -> BlobsSidecar beacon_block_root=hash_tree_root(block), beacon_block_slot=block.slot, blobs=blobs, - kzg_aggregated_proof=compute_proof_from_blobs(blobs), + kzg_aggregated_proof=compute_aggregate_kzg_proof(blobs), ) ``` diff --git a/tests/core/pyspec/eth2spec/test/altair/light_client/test_sync.py b/tests/core/pyspec/eth2spec/test/altair/light_client/test_sync.py index 47348717b5..4621a27723 100644 --- a/tests/core/pyspec/eth2spec/test/altair/light_client/test_sync.py +++ b/tests/core/pyspec/eth2spec/test/altair/light_client/test_sync.py @@ -63,11 +63,11 @@ def get_checks(store): return { "finalized_header": { 'slot': int(store.finalized_header.slot), - 'root': encode_hex(store.finalized_header.hash_tree_root()), + 'beacon_root': encode_hex(store.finalized_header.hash_tree_root()), }, "optimistic_header": { 'slot': int(store.optimistic_header.slot), - 'root': encode_hex(store.optimistic_header.hash_tree_root()), + 'beacon_root': encode_hex(store.optimistic_header.hash_tree_root()), }, } @@ -110,7 +110,7 @@ def compute_start_slot_at_sync_committee_period(spec, sync_committee_period): def compute_start_slot_at_next_sync_committee_period(spec, state): - sync_committee_period = spec.compute_sync_committee_period(spec.compute_epoch_at_slot(state.slot)) + sync_committee_period = spec.compute_sync_committee_period_at_slot(state.slot) return compute_start_slot_at_sync_committee_period(spec, sync_committee_period + 1) diff --git a/tests/core/pyspec/eth2spec/test/altair/unittests/light_client/test_sync_protocol.py b/tests/core/pyspec/eth2spec/test/altair/unittests/light_client/test_sync_protocol.py index e7845292af..bf09cc30ec 100644 --- a/tests/core/pyspec/eth2spec/test/altair/unittests/light_client/test_sync_protocol.py +++ b/tests/core/pyspec/eth2spec/test/altair/unittests/light_client/test_sync_protocol.py @@ -37,14 +37,14 @@ def test_process_light_client_update_not_timeout(spec, state): # Ensure that finality checkpoint is genesis assert state.finalized_checkpoint.epoch == 0 # Finality is unchanged - finality_header = spec.BeaconBlockHeader() + finalized_header = spec.BeaconBlockHeader() finality_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))] update = spec.LightClientUpdate( attested_header=attested_header, next_sync_committee=next_sync_committee, next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finality_header, + finalized_header=finalized_header, finality_branch=finality_branch, sync_aggregate=sync_aggregate, signature_slot=signature_slot, @@ -68,8 +68,8 @@ def test_process_light_client_update_at_period_boundary(spec, state): # Forward to slot before next sync committee period so that next block is final one in period next_slots(spec, state, spec.UPDATE_TIMEOUT - 2) - store_period = spec.compute_sync_committee_period(spec.compute_epoch_at_slot(store.optimistic_header.slot)) - update_period = spec.compute_sync_committee_period(spec.compute_epoch_at_slot(state.slot)) + store_period = spec.compute_sync_committee_period_at_slot(store.optimistic_header.slot) + update_period = spec.compute_sync_committee_period_at_slot(state.slot) assert store_period == update_period attested_block = state_transition_with_full_block(spec, state, False, False) @@ -81,14 +81,14 @@ def test_process_light_client_update_at_period_boundary(spec, state): next_sync_committee_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))] # Finality is unchanged - finality_header = spec.BeaconBlockHeader() + finalized_header = spec.BeaconBlockHeader() finality_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))] update = spec.LightClientUpdate( attested_header=attested_header, next_sync_committee=next_sync_committee, next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finality_header, + finalized_header=finalized_header, finality_branch=finality_branch, sync_aggregate=sync_aggregate, signature_slot=signature_slot, @@ -112,8 +112,8 @@ def test_process_light_client_update_timeout(spec, state): # Forward to next sync committee period next_slots(spec, state, spec.UPDATE_TIMEOUT) - store_period = spec.compute_sync_committee_period(spec.compute_epoch_at_slot(store.optimistic_header.slot)) - update_period = spec.compute_sync_committee_period(spec.compute_epoch_at_slot(state.slot)) + store_period = spec.compute_sync_committee_period_at_slot(store.optimistic_header.slot) + update_period = spec.compute_sync_committee_period_at_slot(state.slot) assert store_period + 1 == update_period attested_block = state_transition_with_full_block(spec, state, False, False) @@ -126,14 +126,14 @@ def test_process_light_client_update_timeout(spec, state): next_sync_committee = state.next_sync_committee next_sync_committee_branch = spec.compute_merkle_proof_for_state(state, spec.NEXT_SYNC_COMMITTEE_INDEX) # Finality is unchanged - finality_header = spec.BeaconBlockHeader() + finalized_header = spec.BeaconBlockHeader() finality_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.FINALIZED_ROOT_INDEX))] update = spec.LightClientUpdate( attested_header=attested_header, next_sync_committee=next_sync_committee, next_sync_committee_branch=next_sync_committee_branch, - finalized_header=finality_header, + finalized_header=finalized_header, finality_branch=finality_branch, sync_aggregate=sync_aggregate, signature_slot=signature_slot, @@ -164,8 +164,8 @@ def test_process_light_client_update_finality_updated(spec, state): # Ensure that finality checkpoint has changed assert state.finalized_checkpoint.epoch == 3 # Ensure that it's same period - store_period = spec.compute_sync_committee_period(spec.compute_epoch_at_slot(store.optimistic_header.slot)) - update_period = spec.compute_sync_committee_period(spec.compute_epoch_at_slot(state.slot)) + store_period = spec.compute_sync_committee_period_at_slot(store.optimistic_header.slot) + update_period = spec.compute_sync_committee_period_at_slot(state.slot) assert store_period == update_period attested_block = blocks[-1] diff --git a/tests/core/pyspec/eth2spec/test/eip4844/unittests/polynomial_commitments/__init__.py b/tests/core/pyspec/eth2spec/test/eip4844/unittests/polynomial_commitments/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/core/pyspec/eth2spec/test/eip4844/unittests/polynomial_commitments/test_polynomial_commitments.py b/tests/core/pyspec/eth2spec/test/eip4844/unittests/polynomial_commitments/test_polynomial_commitments.py new file mode 100644 index 0000000000..dea6aeb8c9 --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/eip4844/unittests/polynomial_commitments/test_polynomial_commitments.py @@ -0,0 +1,20 @@ +from eth2spec.test.context import ( + spec_state_test, + with_eip4844_and_later, +) +from eth2spec.test.helpers.sharding import ( + get_sample_blob, +) + + +@with_eip4844_and_later +@spec_state_test +def test_verify_kzg_proof(spec, state): + x = 3 + blob = get_sample_blob(spec) + commitment = spec.blob_to_kzg_commitment(blob) + polynomial = spec.blob_to_polynomial(blob) + proof = spec.compute_kzg_proof(polynomial, x) + + y = spec.evaluate_polynomial_in_evaluation_form(polynomial, x) + assert spec.verify_kzg_proof(commitment, x, y, proof) diff --git a/tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/test_validator.py b/tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/test_validator.py index 680a0a9c4c..634daca2d5 100644 --- a/tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/test_validator.py +++ b/tests/core/pyspec/eth2spec/test/eip4844/unittests/validator/test_validator.py @@ -10,25 +10,9 @@ ) from eth2spec.test.helpers.sharding import ( get_sample_opaque_tx, - get_sample_blob, ) -@with_eip4844_and_later -@spec_state_test -def test_verify_kzg_proof(spec, state): - x = 3 - polynomial = get_sample_blob(spec) - polynomial = [int(i) for i in polynomial] - commitment = spec.blob_to_kzg_commitment(polynomial) - - # Get the proof - proof = spec.compute_kzg_proof(polynomial, x) - - y = spec.evaluate_polynomial_in_evaluation_form(polynomial, x) - assert spec.verify_kzg_proof(commitment, x, y, proof) - - def _run_validate_blobs_sidecar_test(spec, state, blob_count): block = build_empty_block_for_next_slot(spec, state) opaque_tx, blobs, blob_kzg_commitments = get_sample_opaque_tx(spec, blob_count=blob_count) diff --git a/tests/core/pyspec/eth2spec/test/helpers/sharding.py b/tests/core/pyspec/eth2spec/test/helpers/sharding.py index 6c90153fca..3ce3215eb4 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/sharding.py +++ b/tests/core/pyspec/eth2spec/test/helpers/sharding.py @@ -53,10 +53,16 @@ def get_sample_blob(spec, rng=None): if rng is None: rng = random.Random(5566) - return spec.Blob([ + values = [ rng.randint(0, spec.BLS_MODULUS - 1) for _ in range(spec.FIELD_ELEMENTS_PER_BLOB) - ]) + ] + + b = bytes() + for v in values: + b += v.to_bytes(32, spec.ENDIANNESS) + + return spec.Blob(b) def get_sample_opaque_tx(spec, blob_count=1, rng=None): diff --git a/tests/formats/light_client/sync.md b/tests/formats/light_client/sync.md index ad597fa1db..4d7162c3bc 100644 --- a/tests/formats/light_client/sync.md +++ b/tests/formats/light_client/sync.md @@ -26,11 +26,11 @@ Each step includes checks to verify the expected impact on the `store` object. ```yaml finalized_header: { slot: int, -- Integer value from store.finalized_header.slot - root: string, -- Encoded 32-byte value from store.finalized_header.hash_tree_root() + beacon_root: string, -- Encoded 32-byte value from store.finalized_header.hash_tree_root() } optimistic_header: { slot: int, -- Integer value from store.optimistic_header.slot - root: string, -- Encoded 32-byte value from store.optimistic_header.hash_tree_root() + beacon_root: string, -- Encoded 32-byte value from store.optimistic_header.hash_tree_root() } ```