Merge branch 'dev' into pr3034

This commit is contained in:
Hsiao-Wei Wang 2023-10-26 23:51:35 +08:00
commit d8440f8bb4
No known key found for this signature in database
GPG Key ID: AE3D6B174F971DE4
31 changed files with 8725 additions and 115 deletions

View File

@ -212,7 +212,7 @@ gen_kzg_setups:
if ! test -d venv; then python3 -m venv venv; fi; \
. venv/bin/activate; \
pip3 install -r requirements.txt; \
python3 ./gen_kzg_trusted_setups.py --secret=1337 --g1-length=4 --g2-length=65 --output-dir ${CURRENT_DIR}/presets/minimal/trusted_setups; \
python3 ./gen_kzg_trusted_setups.py --secret=1337 --g1-length=4096 --g2-length=65 --output-dir ${CURRENT_DIR}/presets/minimal/trusted_setups; \
python3 ./gen_kzg_trusted_setups.py --secret=1337 --g1-length=4096 --g2-length=65 --output-dir ${CURRENT_DIR}/presets/mainnet/trusted_setups
# For any generator, build it using the run_generator function.

View File

@ -87,7 +87,8 @@ EJECTION_BALANCE: 16000000000
MIN_PER_EPOCH_CHURN_LIMIT: 4
# 2**16 (= 65,536)
CHURN_LIMIT_QUOTIENT: 65536
# [New in Deneb:EIP7514] 2**3 (= 8)
MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 8
# Fork choice
# ---------------------------------------------------------------

View File

@ -82,10 +82,12 @@ INACTIVITY_SCORE_BIAS: 4
INACTIVITY_SCORE_RECOVERY_RATE: 16
# 2**4 * 10**9 (= 16,000,000,000) Gwei
EJECTION_BALANCE: 16000000000
# 2**2 (= 4)
MIN_PER_EPOCH_CHURN_LIMIT: 4
# [customized] more easily demonstrate the difference between this value and the activation churn limit
MIN_PER_EPOCH_CHURN_LIMIT: 2
# [customized] scale queue churn at much lower validator counts for testing
CHURN_LIMIT_QUOTIENT: 32
# [New in Deneb:EIP7514] [customized]
MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: 4
# Fork choice

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -2,8 +2,8 @@
# Misc
# ---------------------------------------------------------------
# [customized]
FIELD_ELEMENTS_PER_BLOB: 4
# `uint64(4096)`
FIELD_ELEMENTS_PER_BLOB: 4096
# [customized]
MAX_BLOB_COMMITMENTS_PER_BLOCK: 16
# `uint64(6)`

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -108,21 +108,14 @@ def _is_constant_id(name: str) -> bool:
def _load_kzg_trusted_setups(preset_name):
"""
[TODO] it's not the final mainnet trusted setup.
We will update it after the KZG ceremony.
"""
file_path = str(Path(__file__).parent) + '/presets/' + preset_name + '/trusted_setups/testing_trusted_setups.json'
trusted_setups_file_path = str(Path(__file__).parent) + '/presets/' + preset_name + '/trusted_setups/trusted_setup_4096.json'
with open(file_path, 'r') as f:
with open(trusted_setups_file_path, 'r') as f:
json_data = json.load(f)
trusted_setup_G1_lagrange = json_data['g1_lagrange']
trusted_setup_G2_monomial = json_data['g2_monomial']
trusted_setup_G1 = json_data['setup_G1']
trusted_setup_G2 = json_data['setup_G2']
trusted_setup_G1_lagrange = json_data['setup_G1_lagrange']
roots_of_unity = json_data['roots_of_unity']
return trusted_setup_G1, trusted_setup_G2, trusted_setup_G1_lagrange, roots_of_unity
return trusted_setup_G2_monomial, trusted_setup_G1_lagrange
ALL_KZG_SETUPS = {
@ -158,10 +151,8 @@ def _parse_value(name: str, typed_value: str, type_hint: Optional[str] = None) -
def _update_constant_vars_with_kzg_setups(constant_vars, preset_name):
comment = "noqa: E501"
kzg_setups = ALL_KZG_SETUPS[preset_name]
constant_vars['KZG_SETUP_G1'] = VariableDefinition(constant_vars['KZG_SETUP_G1'].value, str(kzg_setups[0]), comment, None)
constant_vars['KZG_SETUP_G2'] = VariableDefinition(constant_vars['KZG_SETUP_G2'].value, str(kzg_setups[1]), comment, None)
constant_vars['KZG_SETUP_LAGRANGE'] = VariableDefinition(constant_vars['KZG_SETUP_LAGRANGE'].value, str(kzg_setups[2]), comment, None)
constant_vars['ROOTS_OF_UNITY'] = VariableDefinition(constant_vars['ROOTS_OF_UNITY'].value, str(kzg_setups[3]), comment, None)
constant_vars['KZG_SETUP_G2_MONOMIAL'] = VariableDefinition(constant_vars['KZG_SETUP_G2_MONOMIAL'].value, str(kzg_setups[0]), comment, None)
constant_vars['KZG_SETUP_G1_LAGRANGE'] = VariableDefinition(constant_vars['KZG_SETUP_G1_LAGRANGE'].value, str(kzg_setups[1]), comment, None)
def get_spec(file_name: Path, preset: Dict[str, str], config: Dict[str, str], preset_name=str) -> SpecObject:

View File

@ -52,7 +52,7 @@ Up to `MAX_CUSTODY_KEY_REVEALS`, [`CustodyKeyReveal`](./beacon-chain.md#custodyk
##### Early derived secret reveals
Up to `MAX_EARLY_DERIVED_SECRET_REVEALS`, [`EarlyDerivedSecretReveal`](./beacon-chain.md#earlyderivedsecretreveal) objects can be included in the `block`. The early derived secret reveals must satisfy the verification conditions found in [early derived secret reveal processing](beacon-chain.md#custody-key-reveals). The validator receives a small "whistleblower" reward for each early derived secrete reveal included.
Up to `MAX_EARLY_DERIVED_SECRET_REVEALS`, [`EarlyDerivedSecretReveal`](./beacon-chain.md#earlyderivedsecretreveal) objects can be included in the `block`. The early derived secret reveals must satisfy the verification conditions found in [early derived secret reveal processing](beacon-chain.md#custody-key-reveals). The validator receives a small "whistleblower" reward for each early derived secret reveal included.
#### Construct attestation

View File

@ -415,38 +415,21 @@ def get_initial_tracker(k: BLSFieldElement) -> WhiskTracker:
```
```python
def apply_deposit(state: BeaconState,
pubkey: BLSPubkey,
withdrawal_credentials: Bytes32,
amount: uint64,
signature: BLSSignature) -> None:
validator_pubkeys = [validator.pubkey for validator in state.validators]
if pubkey not in validator_pubkeys:
# Verify the deposit signature (proof of possession) which is not checked by the deposit contract
deposit_message = DepositMessage(
pubkey=pubkey,
withdrawal_credentials=withdrawal_credentials,
amount=amount,
)
domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks
signing_root = compute_signing_root(deposit_message, domain)
# Initialize validator if the deposit signature is valid
if bls.Verify(pubkey, signing_root, signature):
index = get_index_for_new_validator(state)
validator = get_validator_from_deposit(pubkey, withdrawal_credentials, amount)
set_or_append_list(state.validators, index, validator)
set_or_append_list(state.balances, index, amount)
set_or_append_list(state.previous_epoch_participation, index, ParticipationFlags(0b0000_0000))
set_or_append_list(state.current_epoch_participation, index, ParticipationFlags(0b0000_0000))
set_or_append_list(state.inactivity_scores, index, uint64(0))
# [New in Whisk]
k = get_unique_whisk_k(state, ValidatorIndex(len(state.validators) - 1))
state.whisk_trackers.append(get_initial_tracker(k))
state.whisk_k_commitments.append(get_k_commitment(k))
else:
# Increase balance by deposit amount
index = ValidatorIndex(validator_pubkeys.index(pubkey))
increase_balance(state, index, amount)
def add_validator_to_registry(state: BeaconState,
pubkey: BLSPubkey,
withdrawal_credentials: Bytes32,
amount: uint64) -> None:
index = get_index_for_new_validator(state)
validator = get_validator_from_deposit(pubkey, withdrawal_credentials, amount)
set_or_append_list(state.validators, index, validator)
set_or_append_list(state.balances, index, amount)
set_or_append_list(state.previous_epoch_participation, index, ParticipationFlags(0b0000_0000))
set_or_append_list(state.current_epoch_participation, index, ParticipationFlags(0b0000_0000))
set_or_append_list(state.inactivity_scores, index, uint64(0))
# [New in Whisk]
k = get_unique_whisk_k(state, ValidatorIndex(len(state.validators) - 1))
state.whisk_trackers.append(get_initial_tracker(k))
state.whisk_k_commitments.append(get_k_commitment(k))
```
### `get_beacon_proposer_index`

View File

@ -76,7 +76,7 @@ Alias `block = signed_beacon_block.message`, `execution_payload = block.body.exe
then validate the following:
- _[REJECT]_ The block's execution payload timestamp is correct with respect to the slot
-- i.e. `execution_payload.timestamp == compute_timestamp_at_slot(state, block.slot)`.
- If `exection_payload` verification of block's parent by an execution node is *not* complete:
- If `execution_payload` verification of block's parent by an execution node is *not* complete:
- [REJECT] The block's parent (defined by `block.parent_root`) passes all
validation (excluding execution node verification of the `block.body.execution_payload`).
- otherwise:

View File

@ -16,6 +16,7 @@
- [Preset](#preset)
- [Execution](#execution)
- [Configuration](#configuration)
- [Validator cycle](#validator-cycle)
- [Containers](#containers)
- [Extended containers](#extended-containers)
- [`BeaconBlockBody`](#beaconblockbody)
@ -26,6 +27,7 @@
- [`kzg_commitment_to_versioned_hash`](#kzg_commitment_to_versioned_hash)
- [Beacon state accessors](#beacon-state-accessors)
- [Modified `get_attestation_participation_flag_indices`](#modified-get_attestation_participation_flag_indices)
- [New `get_validator_activation_churn_limit`](#new-get_validator_activation_churn_limit)
- [Beacon chain state transition function](#beacon-chain-state-transition-function)
- [Execution engine](#execution-engine)
- [Request data](#request-data)
@ -40,6 +42,8 @@
- [Execution payload](#execution-payload)
- [Modified `process_execution_payload`](#modified-process_execution_payload)
- [Modified `process_voluntary_exit`](#modified-process_voluntary_exit)
- [Epoch processing](#epoch-processing)
- [Registry updates](#registry-updates)
- [Testing](#testing)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@ -50,8 +54,9 @@
Deneb is a consensus-layer upgrade containing a number of features. Including:
* [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788): Beacon block root in the EVM
* [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844): Shard Blob Transactions scale data-availability of Ethereum in a simple, forwards-compatible manner
* [EIP-7044](https://github.com/ethereum/EIPs/pull/7044): Perpetually Valid Signed Voluntary Exits
* [EIP-7044](https://eips.ethereum.org/EIPS/eip-7044): Perpetually Valid Signed Voluntary Exits
* [EIP-7045](https://eips.ethereum.org/EIPS/eip-7045): Increase Max Attestation Inclusion Slot
* [EIP-7514](https://eips.ethereum.org/EIPS/eip-7514): Add Max Epoch Churn Limit
## Custom types
@ -89,6 +94,12 @@ and are limited by `MAX_BLOB_GAS_PER_BLOCK // GAS_PER_BLOB`. However the CL limi
## Configuration
### Validator cycle
| Name | Value |
| - | - |
| `MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT` | `uint64(2**3)` (= 8) |
## Containers
### Extended containers
@ -211,6 +222,16 @@ def get_attestation_participation_flag_indices(state: BeaconState,
return participation_flag_indices
```
#### New `get_validator_activation_churn_limit`
```python
def get_validator_activation_churn_limit(state: BeaconState) -> uint64:
"""
Return the validator activation churn limit for the current epoch.
"""
return min(MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT, get_validator_churn_limit(state))
```
## Beacon chain state transition function
### Execution engine
@ -415,6 +436,38 @@ def process_voluntary_exit(state: BeaconState, signed_voluntary_exit: SignedVolu
initiate_validator_exit(state, voluntary_exit.validator_index)
```
### Epoch processing
#### Registry updates
*Note*: The function `process_registry_updates` is modified to utilize `get_validator_activation_churn_limit()` to rate limit the activation queue for EIP-7514.
```python
def process_registry_updates(state: BeaconState) -> None:
# Process activation eligibility and ejections
for index, validator in enumerate(state.validators):
if is_eligible_for_activation_queue(validator):
validator.activation_eligibility_epoch = get_current_epoch(state) + 1
if (
is_active_validator(validator, get_current_epoch(state))
and validator.effective_balance <= EJECTION_BALANCE
):
initiate_validator_exit(state, ValidatorIndex(index))
# Queue validators eligible for activation and not yet dequeued for activation
activation_queue = sorted([
index for index, validator in enumerate(state.validators)
if is_eligible_for_activation(state, validator)
# Order by the sequence of activation_eligibility_epoch setting and then index
], key=lambda index: (state.validators[index].activation_eligibility_epoch, index))
# Dequeued validators for activation up to activation churn limit
# [Modified in Deneb:EIP7514]
for index in activation_queue[:get_validator_activation_churn_limit(state)]:
validator = state.validators[index]
validator.activation_epoch = compute_activation_exit_epoch(get_current_epoch(state))
```
## Testing
*Note*: The function `initialize_beacon_state_from_eth1` is modified for pure Deneb testing only.

View File

@ -47,6 +47,8 @@ Initially, verification requires every verifying actor to retrieve all matching
The block MUST NOT be considered valid until all valid `Blob`s have been downloaded. Blocks that have been previously validated as available SHOULD be considered available even if the associated `Blob`s have subsequently been pruned.
*Note*: Extraneous or invalid Blobs (in addition to KZG expected/referenced valid blobs) received on the p2p network MUST NOT invalidate a block that is otherwise valid and available.
```python
def is_data_available(beacon_block_root: Root, blob_kzg_commitments: Sequence[KZGCommitment]) -> bool:
# `retrieve_blobs_and_proofs` is implementation and context dependent
@ -91,6 +93,8 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
# [New in Deneb:EIP4844]
# Check if blob data is available
# If not, this block MAY be queued and subsequently considered when blob data becomes available
# *Note*: Extraneous or invalid Blobs (in addition to the expected/referenced valid blobs)
# received on the p2p network MUST NOT invalidate a block that is otherwise valid and available
assert is_data_available(hash_tree_root(block), block.body.blob_kzg_commitments)
# Check the block is valid and compute the post-state

View File

@ -148,6 +148,7 @@ This topic is used to propagate signed blob sidecars, where each blob index maps
The following validations MUST pass before forwarding the `signed_blob_sidecar` on the network, assuming the alias `sidecar = signed_blob_sidecar.message`:
- _[REJECT]_ The sidecar's index is consistent with `MAX_BLOBS_PER_BLOCK` -- i.e. `sidecar.index < MAX_BLOBS_PER_BLOCK`.
- _[REJECT]_ The sidecar is for the correct subnet -- i.e. `compute_subnet_for_blob_sidecar(sidecar.index) == subnet_id`.
- _[IGNORE]_ The sidecar is not from a future slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance) -- i.e. validate that `sidecar.slot <= current_slot` (a client MAY queue future sidecars for processing at the appropriate slot).
- _[IGNORE]_ The sidecar is from a slot greater than the latest finalized slot -- i.e. validate that `sidecar.slot > compute_start_slot_at_epoch(state.finalized_checkpoint.epoch)`

View File

@ -11,7 +11,6 @@
- [Constants](#constants)
- [Preset](#preset)
- [Blob](#blob)
- [Crypto](#crypto)
- [Trusted setup](#trusted-setup)
- [Helper functions](#helper-functions)
- [Bit-reversal permutation](#bit-reversal-permutation)
@ -30,6 +29,7 @@
- [`div`](#div)
- [`g1_lincomb`](#g1_lincomb)
- [`compute_powers`](#compute_powers)
- [`compute_roots_of_unity`](#compute_roots_of_unity)
- [Polynomials](#polynomials)
- [`evaluate_polynomial_in_evaluation_form`](#evaluate_polynomial_in_evaluation_form)
- [KZG](#kzg)
@ -78,7 +78,7 @@ Public functions MUST accept raw bytes as input and perform the required cryptog
| `BYTES_PER_BLOB` | `uint64(BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB)` | The number of bytes in a blob |
| `G1_POINT_AT_INFINITY` | `Bytes48(b'\xc0' + b'\x00' * 47)` | Serialized form of the point at infinity on the G1 group |
| `KZG_ENDIANNESS` | `'big'` | The endianness of the field elements including blobs |
| `PRIMITIVE_ROOT_OF_UNITY` | `7` | Primitive root of unity of the BLS12_381 (inner) BLS_MODULUS |
## Preset
@ -90,23 +90,13 @@ Public functions MUST accept raw bytes as input and perform the required cryptog
| `FIAT_SHAMIR_PROTOCOL_DOMAIN` | `b'FSBLOBVERIFY_V1_'` |
| `RANDOM_CHALLENGE_KZG_BATCH_DOMAIN` | `b'RCKZGBATCH___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,
but reusing the `mainnet` settings in public networks is a critical security requirement.
| Name | Value |
| - | - |
| `KZG_SETUP_G2_LENGTH` | `65` |
| `KZG_SETUP_G1` | `Vector[G1Point, FIELD_ELEMENTS_PER_BLOB]`, contents TBD |
| `KZG_SETUP_G2` | `Vector[G2Point, KZG_SETUP_G2_LENGTH]`, contents TBD |
| `KZG_SETUP_LAGRANGE` | `Vector[G1Point, FIELD_ELEMENTS_PER_BLOB]`, contents TBD |
| `KZG_SETUP_G2_MONOMIAL` | `Vector[G2Point, KZG_SETUP_G2_LENGTH]` |
| `KZG_SETUP_G1_LAGRANGE` | `Vector[G1Point, FIELD_ELEMENTS_PER_BLOB]` |
## Helper functions
@ -114,7 +104,7 @@ but reusing the `mainnet` settings in public networks is a critical security req
All polynomials (which are always given in Lagrange form) should be interpreted as being in
bit-reversal permutation. In practice, clients can implement this by storing the lists
`KZG_SETUP_LAGRANGE` and `ROOTS_OF_UNITY` in bit-reversal permutation, so these functions only
`KZG_SETUP_G1_LAGRANGE` and roots of unity in bit-reversal permutation, so these functions only
have to be called once at startup.
#### `is_power_of_two`
@ -299,6 +289,17 @@ def compute_powers(x: BLSFieldElement, n: uint64) -> Sequence[BLSFieldElement]:
return powers
```
#### `compute_roots_of_unity`
```python
def compute_roots_of_unity(order: uint64) -> Sequence[BLSFieldElement]:
"""
Return roots of unity of ``order``.
"""
assert (BLS_MODULUS - 1) % int(order) == 0
root_of_unity = BLSFieldElement(pow(PRIMITIVE_ROOT_OF_UNITY, (BLS_MODULUS - 1) // int(order), BLS_MODULUS))
return compute_powers(root_of_unity, order)
```
### Polynomials
@ -318,7 +319,7 @@ def evaluate_polynomial_in_evaluation_form(polynomial: Polynomial,
assert width == FIELD_ELEMENTS_PER_BLOB
inverse_width = bls_modular_inverse(BLSFieldElement(width))
roots_of_unity_brp = bit_reversal_permutation(ROOTS_OF_UNITY)
roots_of_unity_brp = bit_reversal_permutation(compute_roots_of_unity(FIELD_ELEMENTS_PER_BLOB))
# If we are asked to evaluate within the domain, we already know the answer
if z in roots_of_unity_brp:
@ -346,7 +347,7 @@ def blob_to_kzg_commitment(blob: Blob) -> KZGCommitment:
Public method.
"""
assert len(blob) == BYTES_PER_BLOB
return g1_lincomb(bit_reversal_permutation(KZG_SETUP_LAGRANGE), blob_to_polynomial(blob))
return g1_lincomb(bit_reversal_permutation(KZG_SETUP_G1_LAGRANGE), blob_to_polynomial(blob))
```
#### `verify_kzg_proof`
@ -384,7 +385,10 @@ def verify_kzg_proof_impl(commitment: KZGCommitment,
Verify KZG proof that ``p(z) == y`` where ``p(z)`` is the polynomial represented by ``polynomial_kzg``.
"""
# Verify: P - y = Q * (X - z)
X_minus_z = bls.add(bls.bytes96_to_G2(KZG_SETUP_G2[1]), bls.multiply(bls.G2(), (BLS_MODULUS - z) % BLS_MODULUS))
X_minus_z = bls.add(
bls.bytes96_to_G2(KZG_SETUP_G2_MONOMIAL[1]),
bls.multiply(bls.G2(), (BLS_MODULUS - z) % BLS_MODULUS),
)
P_minus_y = bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1(), (BLS_MODULUS - y) % BLS_MODULUS))
return bls.pairing_check([
[P_minus_y, bls.neg(bls.G2())],
@ -434,7 +438,7 @@ def verify_kzg_proof_batch(commitments: Sequence[KZGCommitment],
C_minus_y_lincomb = g1_lincomb(C_minus_y_as_KZGCommitments, r_powers)
return bls.pairing_check([
[bls.bytes48_to_G1(proof_lincomb), bls.neg(bls.bytes96_to_G2(KZG_SETUP_G2[1]))],
[bls.bytes48_to_G1(proof_lincomb), bls.neg(bls.bytes96_to_G2(KZG_SETUP_G2_MONOMIAL[1]))],
[bls.add(bls.bytes48_to_G1(C_minus_y_lincomb), bls.bytes48_to_G1(proof_z_lincomb)), bls.G2()]
])
```
@ -464,12 +468,12 @@ def compute_quotient_eval_within_domain(z: BLSFieldElement,
) -> BLSFieldElement:
"""
Given `y == p(z)` for a polynomial `p(x)`, compute `q(z)`: the KZG quotient polynomial evaluated at `z` for the
special case where `z` is in `ROOTS_OF_UNITY`.
special case where `z` is in roots of unity.
For more details, read https://dankradfeist.de/ethereum/2021/06/18/pcs-multiproofs.html section "Dividing
when one of the points is zero". The code below computes q(x_m) for the roots of unity special case.
"""
roots_of_unity_brp = bit_reversal_permutation(ROOTS_OF_UNITY)
roots_of_unity_brp = bit_reversal_permutation(compute_roots_of_unity(FIELD_ELEMENTS_PER_BLOB))
result = 0
for i, omega_i in enumerate(roots_of_unity_brp):
if omega_i == z: # skip the evaluation point in the sum
@ -490,7 +494,7 @@ def compute_kzg_proof_impl(polynomial: Polynomial, z: BLSFieldElement) -> Tuple[
"""
Helper function for `compute_kzg_proof()` and `compute_blob_kzg_proof()`.
"""
roots_of_unity_brp = bit_reversal_permutation(ROOTS_OF_UNITY)
roots_of_unity_brp = bit_reversal_permutation(compute_roots_of_unity(FIELD_ELEMENTS_PER_BLOB))
# For all x_i, compute p(x_i) - p(z)
y = evaluate_polynomial_in_evaluation_form(polynomial, z)
@ -510,7 +514,7 @@ def compute_kzg_proof_impl(polynomial: Polynomial, z: BLSFieldElement) -> Tuple[
# Compute: q(x_i) = (p(x_i) - p(z)) / (x_i - z).
quotient_polynomial[i] = div(a, b)
return KZGProof(g1_lincomb(bit_reversal_permutation(KZG_SETUP_LAGRANGE), quotient_polynomial)), y
return KZGProof(g1_lincomb(bit_reversal_permutation(KZG_SETUP_G1_LAGRANGE), quotient_polynomial)), y
```
#### `compute_blob_kzg_proof`

View File

@ -158,11 +158,11 @@ This applies to transports that are natively incapable of multiplexing (e.g. TCP
and is omitted for capable transports (e.g. QUIC).
Two multiplexers are commonplace in libp2p implementations:
[mplex](https://github.com/libp2p/specs/tree/master/mplex) and [yamux](https://github.com/hashicorp/yamux/blob/master/spec.md).
[mplex](https://github.com/libp2p/specs/tree/master/mplex) and [yamux](https://github.com/libp2p/specs/blob/master/yamux/README.md).
Their protocol IDs are, respectively: `/mplex/6.7.0` and `/yamux/1.0.0`.
Clients MUST support [mplex](https://github.com/libp2p/specs/tree/master/mplex)
and MAY support [yamux](https://github.com/hashicorp/yamux/blob/master/spec.md).
and MAY support [yamux](https://github.com/libp2p/specs/blob/master/yamux/README.md).
If both are supported by the client, yamux MUST take precedence during negotiation.
See the [Rationale](#design-decision-rationale) section below for tradeoffs.

View File

@ -1 +1 @@
1.4.0-beta.1
1.4.0-beta.3

View File

@ -373,8 +373,8 @@ def test_invalid_signature_previous_committee(spec, state):
current_epoch = spec.get_current_epoch(state)
old_sync_committee = state.next_sync_committee
epoch_in_future_sync_commitee_period = current_epoch + 2 * spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD
slot_in_future_sync_committee_period = epoch_in_future_sync_commitee_period * spec.SLOTS_PER_EPOCH
epoch_in_future_sync_committee_period = current_epoch + 2 * spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD
slot_in_future_sync_committee_period = epoch_in_future_sync_committee_period * spec.SLOTS_PER_EPOCH
transition_to(spec, state, slot_in_future_sync_committee_period)
# Use the previous sync committee to produce the signature.

View File

@ -162,14 +162,34 @@ def default_balances(spec: Spec):
return [spec.MAX_EFFECTIVE_BALANCE] * num_validators
def scaled_churn_balances(spec: Spec):
def scaled_churn_balances_min_churn_limit(spec: Spec):
"""
Helper method to create enough validators to scale the churn limit.
(This is *firmly* over the churn limit -- thus the +2 instead of just +1)
See the second argument of ``max`` in ``get_validator_churn_limit``.
Usage: `@with_custom_state(balances_fn=scaled_churn_balances, ...)`
Usage: `@with_custom_state(balances_fn=scaled_churn_balances_min_churn_limit, ...)`
"""
num_validators = spec.config.CHURN_LIMIT_QUOTIENT * (2 + spec.config.MIN_PER_EPOCH_CHURN_LIMIT)
num_validators = spec.config.CHURN_LIMIT_QUOTIENT * (spec.config.MIN_PER_EPOCH_CHURN_LIMIT + 2)
return [spec.MAX_EFFECTIVE_BALANCE] * num_validators
def scaled_churn_balances_equal_activation_churn_limit(spec: Spec):
"""
Helper method to create enough validators to scale the churn limit.
(This is *firmly* over the churn limit -- thus the +2 instead of just +1)
Usage: `@with_custom_state(balances_fn=scaled_churn_balances_exceed_activation_churn_limit, ...)`
"""
num_validators = spec.config.CHURN_LIMIT_QUOTIENT * (spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT)
return [spec.MAX_EFFECTIVE_BALANCE] * num_validators
def scaled_churn_balances_exceed_activation_churn_limit(spec: Spec):
"""
Helper method to create enough validators to scale the churn limit.
(This is *firmly* over the churn limit -- thus the +2 instead of just +1)
Usage: `@with_custom_state(balances_fn=scaled_churn_balances_exceed_activation_churn_limit, ...)`
"""
num_validators = spec.config.CHURN_LIMIT_QUOTIENT * (spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT + 2)
return [spec.MAX_EFFECTIVE_BALANCE] * num_validators

View File

@ -0,0 +1,90 @@
from eth2spec.test.helpers.keys import pubkeys
from eth2spec.test.helpers.constants import MINIMAL
from eth2spec.test.context import (
with_deneb_and_later,
spec_test,
spec_state_test,
single_phase,
with_custom_state,
with_presets,
scaled_churn_balances_exceed_activation_churn_limit,
scaled_churn_balances_equal_activation_churn_limit,
)
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with
def run_process_registry_updates(spec, state):
yield from run_epoch_processing_with(spec, state, 'process_registry_updates')
def run_test_activation_churn_limit(spec, state):
mock_activations = spec.get_validator_activation_churn_limit(state) * 2
validator_count_0 = len(state.validators)
for i in range(mock_activations):
index = validator_count_0 + i
validator = spec.Validator(
pubkey=pubkeys[index],
withdrawal_credentials=spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + b'\x00' * 11 + b'\x56' * 20,
activation_eligibility_epoch=0,
activation_epoch=spec.FAR_FUTURE_EPOCH,
exit_epoch=spec.FAR_FUTURE_EPOCH,
withdrawable_epoch=spec.FAR_FUTURE_EPOCH,
effective_balance=spec.MAX_EFFECTIVE_BALANCE,
)
state.validators.append(validator)
state.balances.append(spec.MAX_EFFECTIVE_BALANCE)
state.previous_epoch_participation.append(spec.ParticipationFlags(0b0000_0000))
state.current_epoch_participation.append(spec.ParticipationFlags(0b0000_0000))
state.inactivity_scores.append(0)
state.validators[index].activation_epoch = spec.FAR_FUTURE_EPOCH
churn_limit_0 = spec.get_validator_activation_churn_limit(state)
yield from run_process_registry_updates(spec, state)
# Half should churn in first run of registry update
for i in range(mock_activations):
index = validator_count_0 + i
if index < validator_count_0 + churn_limit_0:
# The eligible validators within the activation churn limit should have been activated
assert state.validators[index].activation_epoch < spec.FAR_FUTURE_EPOCH
else:
assert state.validators[index].activation_epoch == spec.FAR_FUTURE_EPOCH
@with_deneb_and_later
@with_presets([MINIMAL],
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
@spec_test
@with_custom_state(balances_fn=scaled_churn_balances_exceed_activation_churn_limit,
threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@single_phase
def test_activation_churn_limit__greater_than_activation_limit(spec, state):
assert spec.get_validator_activation_churn_limit(state) == spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT
assert spec.get_validator_churn_limit(state) > spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT
yield from run_test_activation_churn_limit(spec, state)
@with_deneb_and_later
@with_presets([MINIMAL],
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
@spec_test
@with_custom_state(balances_fn=scaled_churn_balances_equal_activation_churn_limit,
threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@single_phase
def test_activation_churn_limit__equal_to_activation_limit(spec, state):
assert spec.get_validator_activation_churn_limit(state) == spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT
assert spec.get_validator_churn_limit(state) == spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT
yield from run_test_activation_churn_limit(spec, state)
@with_deneb_and_later
@with_presets([MINIMAL],
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
@spec_state_test
def test_activation_churn_limit__less_than_activation_limit(spec, state):
assert spec.get_validator_activation_churn_limit(state) < spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT
assert spec.get_validator_churn_limit(state) < spec.config.MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT
yield from run_test_activation_churn_limit(spec, state)

View File

@ -58,7 +58,6 @@ def test_simple_blob_data(spec, state):
assert spec.get_head(store) == signed_block.message.hash_tree_root()
# On receiving a block of next epoch
store.time = current_time + spec.config.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH
block, blobs, blob_kzg_proofs = get_block_with_blob(spec, state, rng=rng)
signed_block = state_transition_and_sign_block(spec, state, block)
blob_data = BlobData(blobs, blob_kzg_proofs)

View File

@ -113,7 +113,7 @@ def test_barycentric_outside_domain(spec):
"""
rng = random.Random(5566)
poly_coeff, poly_eval = get_poly_in_both_forms(spec)
roots_of_unity_brp = spec.bit_reversal_permutation(spec.ROOTS_OF_UNITY)
roots_of_unity_brp = spec.bit_reversal_permutation(spec.compute_roots_of_unity(spec.FIELD_ELEMENTS_PER_BLOB))
assert len(poly_coeff) == len(poly_eval) == len(roots_of_unity_brp)
n_samples = 12
@ -139,20 +139,22 @@ def test_barycentric_outside_domain(spec):
@single_phase
def test_barycentric_within_domain(spec):
"""
Test barycentric formula correctness by using it to evaluate a polynomial at all the points of its domain
Test barycentric formula correctness by using it to evaluate a polynomial at various points inside its domain
(the roots of unity).
Then make sure that we would get the same result if we evaluated it from coefficient form without using the
barycentric formula
"""
rng = random.Random(5566)
poly_coeff, poly_eval = get_poly_in_both_forms(spec)
roots_of_unity_brp = spec.bit_reversal_permutation(spec.ROOTS_OF_UNITY)
roots_of_unity_brp = spec.bit_reversal_permutation(spec.compute_roots_of_unity(spec.FIELD_ELEMENTS_PER_BLOB))
assert len(poly_coeff) == len(poly_eval) == len(roots_of_unity_brp)
n = len(poly_coeff)
# Iterate over the entire domain
for i in range(n):
# Iterate over some roots of unity
for i in range(12):
i = rng.randint(0, n - 1)
# Grab a root of unity and use it as the evaluation point
z = int(roots_of_unity_brp[i])
@ -175,15 +177,17 @@ def test_compute_kzg_proof_within_domain(spec):
Create and verify KZG proof that p(z) == y
where z is in the domain of our KZG scheme (i.e. a relevant root of unity).
"""
rng = random.Random(5566)
blob = get_sample_blob(spec)
commitment = spec.blob_to_kzg_commitment(blob)
polynomial = spec.blob_to_polynomial(blob)
roots_of_unity_brp = spec.bit_reversal_permutation(spec.ROOTS_OF_UNITY)
roots_of_unity_brp = spec.bit_reversal_permutation(spec.compute_roots_of_unity(spec.FIELD_ELEMENTS_PER_BLOB))
for i, z in enumerate(roots_of_unity_brp):
# Let's test some roots of unity
for _ in range(6):
z = rng.choice(roots_of_unity_brp)
proof, y = spec.compute_kzg_proof_impl(polynomial, z)
assert spec.verify_kzg_proof_impl(commitment, z, y, proof)

View File

@ -80,7 +80,7 @@ def get_poly_in_both_forms(spec, rng=None):
if rng is None:
rng = random.Random(5566)
roots_of_unity_brp = spec.bit_reversal_permutation(spec.ROOTS_OF_UNITY)
roots_of_unity_brp = spec.bit_reversal_permutation(spec.compute_roots_of_unity(spec.FIELD_ELEMENTS_PER_BLOB))
coeffs = [
rng.randint(0, spec.BLS_MODULUS - 1)

View File

@ -3,7 +3,8 @@ from eth2spec.test.context import (
spec_state_test,
always_bls, with_all_phases, with_presets,
spec_test, single_phase,
with_custom_state, scaled_churn_balances,
with_custom_state,
scaled_churn_balances_min_churn_limit,
)
from eth2spec.test.helpers.keys import pubkey_to_privkey
from eth2spec.test.helpers.voluntary_exits import (
@ -102,7 +103,8 @@ def test_success_exit_queue__min_churn(spec, state):
@with_presets([MINIMAL],
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
@spec_test
@with_custom_state(balances_fn=scaled_churn_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@with_custom_state(balances_fn=scaled_churn_balances_min_churn_limit,
threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@single_phase
def test_success_exit_queue__scaled_churn(spec, state):
churn_limit = spec.get_validator_churn_limit(state)

View File

@ -5,7 +5,7 @@ from eth2spec.test.context import (
spec_test, spec_state_test,
with_all_phases, single_phase,
with_custom_state, with_presets,
scaled_churn_balances,
scaled_churn_balances_min_churn_limit,
)
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with
@ -164,7 +164,8 @@ def test_activation_queue_efficiency_min(spec, state):
@with_presets([MINIMAL],
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
@spec_test
@with_custom_state(balances_fn=scaled_churn_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@with_custom_state(balances_fn=scaled_churn_balances_min_churn_limit,
threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@single_phase
def test_activation_queue_efficiency_scaled(spec, state):
assert spec.get_validator_churn_limit(state) > spec.config.MIN_PER_EPOCH_CHURN_LIMIT
@ -227,7 +228,8 @@ def test_ejection_past_churn_limit_min(spec, state):
@with_presets([MINIMAL],
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
@spec_test
@with_custom_state(balances_fn=scaled_churn_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@with_custom_state(balances_fn=scaled_churn_balances_min_churn_limit,
threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@single_phase
def test_ejection_past_churn_limit_scaled(spec, state):
assert spec.get_validator_churn_limit(state) > spec.config.MIN_PER_EPOCH_CHURN_LIMIT
@ -324,7 +326,8 @@ def test_activation_queue_activation_and_ejection__exceed_churn_limit(spec, stat
@with_presets([MINIMAL],
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
@spec_test
@with_custom_state(balances_fn=scaled_churn_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@with_custom_state(balances_fn=scaled_churn_balances_min_churn_limit,
threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@single_phase
def test_activation_queue_activation_and_ejection__scaled_churn_limit(spec, state):
churn_limit = spec.get_validator_churn_limit(state)
@ -336,7 +339,8 @@ def test_activation_queue_activation_and_ejection__scaled_churn_limit(spec, stat
@with_presets([MINIMAL],
reason="mainnet config leads to larger validator set than limit of public/private keys pre-generated")
@spec_test
@with_custom_state(balances_fn=scaled_churn_balances, threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@with_custom_state(balances_fn=scaled_churn_balances_min_churn_limit,
threshold_fn=lambda spec: spec.config.EJECTION_BALANCE)
@single_phase
def test_activation_queue_activation_and_ejection__exceed_scaled_churn_limit(spec, state):
churn_limit = spec.get_validator_churn_limit(state)

View File

@ -48,8 +48,8 @@ class fastest_bls:
# Flag to make BLS active or not. Used for testing, do not ignore BLS in production unless you know what you are doing.
bls_active = True
# To change bls implementation, default to PyECC for correctness. Milagro is a good faster alternative.
bls = py_ecc_bls
# Default to fastest_bls
bls = fastest_bls
STUB_SIGNATURE = b'\x11' * 96
STUB_PUBKEY = b'\x22' * 48

View File

@ -519,7 +519,7 @@ def create_provider(fork_name: SpecForkName,
preset_name='general',
runner_name='bls',
handler_name=handler_name,
suite_name='small',
suite_name='bls',
case_name=case_name,
case_fn=lambda: [('data', 'data', case_content)]
)

View File

@ -32,7 +32,10 @@ if __name__ == "__main__":
]}
capella_mods = combine_mods(_new_capella_mods, bellatrix_mods)
deneb_mods = capella_mods
_new_deneb_mods = {key: 'eth2spec.test.deneb.epoch_processing.test_process_' + key for key in [
'registry_updates',
]}
deneb_mods = combine_mods(_new_deneb_mods, capella_mods)
eip6110_mods = deneb_mods

View File

@ -67,6 +67,7 @@ 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)]))
@ -79,7 +80,7 @@ BLOB_INVALID_CLOSE = spec.Blob(b''.join(
BLOB_INVALID_LENGTH_PLUS_ONE = BLOB_RANDOM_VALID1 + b"\x00"
BLOB_INVALID_LENGTH_MINUS_ONE = BLOB_RANDOM_VALID1[:-1]
VALID_BLOBS = [BLOB_ALL_ZEROS, BLOB_RANDOM_VALID1, BLOB_RANDOM_VALID2,
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]
@ -88,7 +89,7 @@ FE_VALID2 = field_element_bytes(1)
FE_VALID3 = field_element_bytes(2)
FE_VALID4 = field_element_bytes(pow(5, 1235, spec.BLS_MODULUS))
FE_VALID5 = field_element_bytes(spec.BLS_MODULUS - 1)
FE_VALID6 = field_element_bytes(spec.ROOTS_OF_UNITY[1])
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)
@ -214,6 +215,64 @@ def case03_verify_kzg_proof():
'output': False
}
# Incorrect `G1_POINT_AT_INFINITY` proof
blob = BLOB_RANDOM_VALID1
for z in VALID_FIELD_ELEMENTS:
_, y = spec.compute_kzg_proof(blob, z)
commitment = spec.blob_to_kzg_commitment(blob)
proof = spec.G1_POINT_AT_INFINITY
assert not spec.verify_kzg_proof(commitment, z, y, proof)
prefix = 'verify_kzg_proof_case_incorrect_proof_point_at_infinity'
identifier = f'{encode_hex(hash(blob))}_{encode_hex(z)}'
yield f'{prefix}_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
'input': {
'commitment': encode_hex(commitment),
'z': encode_hex(z),
'y': encode_hex(y),
'proof': encode_hex(proof),
},
'output': False
}
# Correct `G1_POINT_AT_INFINITY` proof for zero poly
blob = BLOB_ALL_ZEROS
for z in VALID_FIELD_ELEMENTS:
_, y = spec.compute_kzg_proof(blob, z)
commitment = spec.blob_to_kzg_commitment(blob)
proof = spec.G1_POINT_AT_INFINITY
assert spec.verify_kzg_proof(commitment, z, y, proof)
prefix = 'verify_kzg_proof_case_correct_proof_point_at_infinity_for_zero_poly'
identifier = f'{encode_hex(hash(blob))}_{encode_hex(z)}'
yield f'{prefix}_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
'input': {
'commitment': encode_hex(commitment),
'z': encode_hex(z),
'y': encode_hex(y),
'proof': encode_hex(proof),
},
'output': True
}
# Correct `G1_POINT_AT_INFINITY` proof for poly of all twos
blob = BLOB_ALL_TWOS
for z in VALID_FIELD_ELEMENTS:
_, y = spec.compute_kzg_proof(blob, z)
commitment = spec.blob_to_kzg_commitment(blob)
proof = spec.G1_POINT_AT_INFINITY
assert spec.verify_kzg_proof(commitment, z, y, proof)
assert y == field_element_bytes(2)
prefix = 'verify_kzg_proof_case_correct_proof_point_at_infinity_for_twos_poly'
identifier = f'{encode_hex(hash(blob))}_{encode_hex(z)}'
yield f'{prefix}_{(hash(bytes(identifier, "utf-8"))[:8]).hex()}', {
'input': {
'commitment': encode_hex(commitment),
'z': encode_hex(z),
'y': encode_hex(y),
'proof': encode_hex(proof),
},
'output': True
}
# Edge case: Invalid commitment
for commitment in INVALID_G1_POINTS:
blob, z = VALID_BLOBS[2], VALID_FIELD_ELEMENTS[1]
@ -354,6 +413,50 @@ def case05_verify_blob_kzg_proof():
'output': False
}
# Incorrect `G1_POINT_AT_INFINITY` proof
blob = BLOB_RANDOM_VALID1
commitment = spec.blob_to_kzg_commitment(blob)
proof = spec.G1_POINT_AT_INFINITY
assert not spec.verify_blob_kzg_proof(blob, commitment, proof)
yield 'verify_blob_kzg_proof_case_incorrect_proof_point_at_infinity', {
'input': {
'blob': encode_hex(blob),
'commitment': encode_hex(commitment),
'proof': encode_hex(proof),
},
'output': False
}
# Correct `G1_POINT_AT_INFINITY` proof and commitment for zero poly
blob = BLOB_ALL_ZEROS
commitment = spec.blob_to_kzg_commitment(blob)
proof = spec.G1_POINT_AT_INFINITY
assert commitment == spec.G1_POINT_AT_INFINITY
assert spec.verify_blob_kzg_proof(blob, commitment, proof)
yield 'verify_blob_kzg_proof_case_correct_proof_point_at_infinity_for_zero_poly', {
'input': {
'blob': encode_hex(blob),
'commitment': encode_hex(commitment),
'proof': encode_hex(proof),
},
'output': True
}
# Correct `G1_POINT_AT_INFINITY` proof for all twos poly
blob = BLOB_ALL_TWOS
commitment = spec.blob_to_kzg_commitment(blob)
proof = spec.G1_POINT_AT_INFINITY
assert commitment != spec.G1_POINT_AT_INFINITY
assert spec.verify_blob_kzg_proof(blob, commitment, proof)
yield 'verify_blob_kzg_proof_case_correct_proof_point_at_infinity_for_twos_poly', {
'input': {
'blob': encode_hex(blob),
'commitment': encode_hex(commitment),
'proof': encode_hex(proof),
},
'output': True
}
# Edge case: Invalid blob
for blob in INVALID_BLOBS:
proof = G1
@ -423,7 +526,7 @@ def case06_verify_blob_kzg_proof_batch():
# Incorrect proof
proofs_incorrect = [bls_add_one(proofs[0])] + proofs[1:]
assert not spec.verify_blob_kzg_proof_batch(VALID_BLOBS, commitments, proofs_incorrect)
yield 'verify_blob_kzg_proof_batch_case_invalid_proof', {
yield 'verify_blob_kzg_proof_batch_case_incorrect_proof_add_one', {
'input': {
'blobs': encode_hex_list(VALID_BLOBS),
'commitments': encode_hex_list(commitments),
@ -432,6 +535,20 @@ def case06_verify_blob_kzg_proof_batch():
'output': False
}
# Incorrect `G1_POINT_AT_INFINITY` proof
blob = BLOB_RANDOM_VALID1
commitment = spec.blob_to_kzg_commitment(blob)
proof = spec.G1_POINT_AT_INFINITY
assert not spec.verify_blob_kzg_proof_batch([blob], [commitment], [proof])
yield 'verify_blob_kzg_proof_batch_case_incorrect_proof_point_at_infinity', {
'input': {
'blobs': encode_hex_list([blob]),
'commitments': encode_hex_list([commitment]),
'proofs': encode_hex_list([proof]),
},
'output': False
}
# Edge case: Invalid blobs
for blob in INVALID_BLOBS:
blobs_invalid = VALID_BLOBS[:4] + [blob] + VALID_BLOBS[5:]
@ -527,7 +644,7 @@ def create_provider(fork_name: SpecForkName,
preset_name='general',
runner_name='kzg',
handler_name=handler_name,
suite_name='small',
suite_name='kzg-mainnet',
case_name=case_name,
case_fn=lambda: [('data', 'data', case_content)]
)