mirror of
https://github.com/ethereum/consensus-specs.git
synced 2026-02-01 11:34:55 -05:00
Merge remote-tracking branch 'upstream/dev' into 3207
This commit is contained in:
@@ -85,17 +85,12 @@ This topic is used to propagate new signed and coupled beacon blocks and blobs s
|
||||
|
||||
In addition to the gossip validations for the `beacon_block` topic from prior specifications, the following validations MUST pass before forwarding the `signed_beacon_block_and_blobs_sidecar` on the network.
|
||||
Alias `signed_beacon_block = signed_beacon_block_and_blobs_sidecar.beacon_block`, `block = signed_beacon_block.message`, `execution_payload = block.body.execution_payload`.
|
||||
- _[REJECT]_ The KZG commitments of the blobs are all correctly encoded compressed BLS G1 points
|
||||
-- i.e. `all(bls.KeyValidate(commitment) for commitment in block.body.blob_kzg_commitments)`
|
||||
- _[REJECT]_ The KZG commitments correspond to the versioned hashes in the transactions list
|
||||
-- i.e. `verify_kzg_commitments_against_transactions(block.body.execution_payload.transactions, block.body.blob_kzg_commitments)`
|
||||
|
||||
Alias `sidecar = signed_beacon_block_and_blobs_sidecar.blobs_sidecar`.
|
||||
- _[IGNORE]_ the `sidecar.beacon_block_slot` is for the current slot (with a `MAXIMUM_GOSSIP_CLOCK_DISPARITY` allowance)
|
||||
-- i.e. `sidecar.beacon_block_slot == block.slot`.
|
||||
- _[REJECT]_ the `sidecar.blobs` are all well formatted, i.e. the `BLSFieldElement` in valid range (`x < BLS_MODULUS`).
|
||||
- _[REJECT]_ The KZG proof is a correctly encoded compressed BLS G1 point
|
||||
-- i.e. `bls.KeyValidate(blobs_sidecar.kzg_aggregated_proof)`
|
||||
- _[REJECT]_ The KZG commitments in the block are valid against the provided blobs sidecar
|
||||
-- i.e. `validate_blobs_sidecar(block.slot, hash_tree_root(block), block.body.blob_kzg_commitments, sidecar)`
|
||||
|
||||
@@ -218,7 +213,7 @@ Each _successful_ `response_chunk` MUST contain a single `BlobsSidecar` payload.
|
||||
Clients MUST keep a record of signed blobs sidecars seen on the epoch range
|
||||
`[max(current_epoch - MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS, DENEB_FORK_EPOCH), current_epoch]`
|
||||
where `current_epoch` is defined by the current wall-clock time,
|
||||
and clients MUST support serving requests of blocks on this range.
|
||||
and clients MUST support serving requests of blobs on this range.
|
||||
|
||||
Peers that are unable to reply to blobs sidecars requests within the `MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUESTS`
|
||||
epoch range SHOULD respond with error code `3: ResourceUnavailable`.
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
- [BLS12-381 helpers](#bls12-381-helpers)
|
||||
- [`hash_to_bls_field`](#hash_to_bls_field)
|
||||
- [`bytes_to_bls_field`](#bytes_to_bls_field)
|
||||
- [`validate_kzg_g1`](#validate_kzg_g1)
|
||||
- [`bytes_to_kzg_commitment`](#bytes_to_kzg_commitment)
|
||||
- [`bytes_to_kzg_proof`](#bytes_to_kzg_proof)
|
||||
- [`blob_to_polynomial`](#blob_to_polynomial)
|
||||
- [`compute_challenges`](#compute_challenges)
|
||||
- [`bls_modular_inverse`](#bls_modular_inverse)
|
||||
@@ -35,6 +38,7 @@
|
||||
- [`verify_kzg_proof`](#verify_kzg_proof)
|
||||
- [`verify_kzg_proof_impl`](#verify_kzg_proof_impl)
|
||||
- [`compute_kzg_proof`](#compute_kzg_proof)
|
||||
- [`compute_kzg_proof_impl`](#compute_kzg_proof_impl)
|
||||
- [`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)
|
||||
@@ -48,17 +52,19 @@ This document specifies basic polynomial operations and KZG polynomial commitmen
|
||||
|
||||
Functions flagged as "Public method" MUST be provided by the underlying KZG library as public functions. All other functions are private functions used internally by the KZG library.
|
||||
|
||||
Public functions MUST accept raw bytes as input and perform the required cryptographic normalization before invoking any internal functions.
|
||||
|
||||
## Custom types
|
||||
|
||||
| Name | SSZ equivalent | Description |
|
||||
| - | - | - |
|
||||
| `G1Point` | `Bytes48` | |
|
||||
| `G2Point` | `Bytes96` | |
|
||||
| `BLSFieldElement` | `uint256` | `x < BLS_MODULUS` |
|
||||
| `KZGCommitment` | `Bytes48` | Same as BLS standard "is valid pubkey" check but also allows `0x00..00` for point-at-infinity |
|
||||
| `BLSFieldElement` | `uint256` | Validation: `x < BLS_MODULUS` |
|
||||
| `KZGCommitment` | `Bytes48` | Validation: Perform [BLS standard's](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature-04#section-2.5) "KeyValidate" check but do allow the identity point |
|
||||
| `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 |
|
||||
| `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
|
||||
|
||||
@@ -66,6 +72,8 @@ Functions flagged as "Public method" MUST be provided by the underlying KZG libr
|
||||
| - | - | - |
|
||||
| `BLS_MODULUS` | `52435875175126190479447740508185965837690552500527637822603658699938581184513` | Scalar field modulus of BLS12-381 |
|
||||
| `BYTES_PER_FIELD_ELEMENT` | `uint64(32)` | Bytes used to encode a BLS scalar field element |
|
||||
| `G1_POINT_AT_INFINITY` | `Bytes48(b'\xc0' + b'\x00' * 47)` | Serialized form of the point at infinity on the G1 group |
|
||||
|
||||
|
||||
## Preset
|
||||
|
||||
@@ -156,7 +164,7 @@ def hash_to_bls_field(data: bytes) -> BLSFieldElement:
|
||||
```python
|
||||
def bytes_to_bls_field(b: Bytes32) -> BLSFieldElement:
|
||||
"""
|
||||
Convert 32-byte value to a BLS scalar field element.
|
||||
Convert untrusted bytes to a trusted and validated BLS scalar field element.
|
||||
This function does not accept inputs greater than the BLS modulus.
|
||||
"""
|
||||
field_element = int.from_bytes(b, ENDIANNESS)
|
||||
@@ -164,6 +172,42 @@ def bytes_to_bls_field(b: Bytes32) -> BLSFieldElement:
|
||||
return BLSFieldElement(field_element)
|
||||
```
|
||||
|
||||
|
||||
#### `validate_kzg_g1`
|
||||
|
||||
```python
|
||||
def validate_kzg_g1(b: Bytes48) -> None:
|
||||
"""
|
||||
Perform BLS validation required by the types `KZGProof` and `KZGCommitment`.
|
||||
"""
|
||||
if b == G1_POINT_AT_INFINITY:
|
||||
return
|
||||
|
||||
assert bls.KeyValidate(b)
|
||||
```
|
||||
|
||||
#### `bytes_to_kzg_commitment`
|
||||
|
||||
```python
|
||||
def bytes_to_kzg_commitment(b: Bytes48) -> KZGCommitment:
|
||||
"""
|
||||
Convert untrusted bytes into a trusted and validated KZGCommitment.
|
||||
"""
|
||||
validate_kzg_g1(b)
|
||||
return KZGCommitment(b)
|
||||
```
|
||||
|
||||
#### `bytes_to_kzg_proof`
|
||||
|
||||
```python
|
||||
def bytes_to_kzg_proof(b: Bytes48) -> KZGProof:
|
||||
"""
|
||||
Convert untrusted bytes into a trusted and validated KZGProof.
|
||||
"""
|
||||
validate_kzg_g1(b)
|
||||
return KZGProof(b)
|
||||
```
|
||||
|
||||
#### `blob_to_polynomial`
|
||||
|
||||
```python
|
||||
@@ -335,47 +379,61 @@ def blob_to_kzg_commitment(blob: Blob) -> KZGCommitment:
|
||||
#### `verify_kzg_proof`
|
||||
|
||||
```python
|
||||
def verify_kzg_proof(polynomial_kzg: KZGCommitment,
|
||||
def verify_kzg_proof(commitment_bytes: Bytes48,
|
||||
z: Bytes32,
|
||||
y: Bytes32,
|
||||
kzg_proof: KZGProof) -> bool:
|
||||
proof_bytes: Bytes48) -> bool:
|
||||
"""
|
||||
Verify KZG proof that ``p(z) == y`` where ``p(z)`` is the polynomial represented by ``polynomial_kzg``.
|
||||
Receives inputs as bytes.
|
||||
Public method.
|
||||
"""
|
||||
return verify_kzg_proof_impl(polynomial_kzg, bytes_to_bls_field(z), bytes_to_bls_field(y), kzg_proof)
|
||||
return verify_kzg_proof_impl(bytes_to_kzg_commitment(commitment_bytes),
|
||||
bytes_to_bls_field(z),
|
||||
bytes_to_bls_field(y),
|
||||
bytes_to_kzg_proof(proof_bytes))
|
||||
```
|
||||
|
||||
|
||||
#### `verify_kzg_proof_impl`
|
||||
|
||||
```python
|
||||
def verify_kzg_proof_impl(polynomial_kzg: KZGCommitment,
|
||||
def verify_kzg_proof_impl(commitment: KZGCommitment,
|
||||
z: BLSFieldElement,
|
||||
y: BLSFieldElement,
|
||||
kzg_proof: KZGProof) -> bool:
|
||||
proof: KZGProof) -> bool:
|
||||
"""
|
||||
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))
|
||||
P_minus_y = bls.add(bls.bytes48_to_G1(polynomial_kzg), bls.multiply(bls.G1, BLS_MODULUS - y))
|
||||
P_minus_y = bls.add(bls.bytes48_to_G1(commitment), bls.multiply(bls.G1, BLS_MODULUS - y))
|
||||
return bls.pairing_check([
|
||||
[P_minus_y, bls.neg(bls.G2)],
|
||||
[bls.bytes48_to_G1(kzg_proof), X_minus_z]
|
||||
[bls.bytes48_to_G1(proof), X_minus_z]
|
||||
])
|
||||
```
|
||||
|
||||
#### `compute_kzg_proof`
|
||||
|
||||
```python
|
||||
def compute_kzg_proof(polynomial: Polynomial, z: BLSFieldElement) -> KZGProof:
|
||||
def compute_kzg_proof(blob: Blob, z: Bytes32) -> KZGProof:
|
||||
"""
|
||||
Compute KZG proof at point `z` with `polynomial` being in evaluation form.
|
||||
Compute KZG proof at point `z` for the polynomial represented by `blob`.
|
||||
Do this by computing the quotient polynomial in evaluation form: q(x) = (p(x) - p(z)) / (x - z).
|
||||
Public method.
|
||||
"""
|
||||
polynomial = blob_to_polynomial(blob)
|
||||
return compute_kzg_proof_impl(polynomial, bytes_to_bls_field(z))
|
||||
```
|
||||
|
||||
#### `compute_kzg_proof_impl`
|
||||
|
||||
```python
|
||||
def compute_kzg_proof_impl(polynomial: Polynomial, z: BLSFieldElement) -> KZGProof:
|
||||
"""
|
||||
Helper function for compute_kzg_proof() and compute_aggregate_kzg_proof().
|
||||
"""
|
||||
y = evaluate_polynomial_in_evaluation_form(polynomial, z)
|
||||
polynomial_shifted = [BLSFieldElement((int(p) - int(y)) % BLS_MODULUS) for p in polynomial]
|
||||
|
||||
@@ -430,28 +488,31 @@ def compute_aggregate_kzg_proof(blobs: Sequence[Blob]) -> KZGProof:
|
||||
blobs,
|
||||
commitments
|
||||
)
|
||||
return compute_kzg_proof(aggregated_poly, evaluation_challenge)
|
||||
return compute_kzg_proof_impl(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: KZGProof) -> bool:
|
||||
commitments_bytes: Sequence[Bytes48],
|
||||
aggregated_proof_bytes: Bytes48) -> bool:
|
||||
"""
|
||||
Given a list of blobs and an aggregated KZG proof, verify that they correspond to the provided commitments.
|
||||
|
||||
Public method.
|
||||
"""
|
||||
commitments = [bytes_to_kzg_commitment(c) for c in commitments_bytes]
|
||||
|
||||
aggregated_poly, aggregated_poly_commitment, evaluation_challenge = compute_aggregated_poly_and_commitment(
|
||||
blobs,
|
||||
expected_kzg_commitments,
|
||||
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_impl(aggregated_poly_commitment, evaluation_challenge, y, kzg_aggregated_proof)
|
||||
aggregated_proof = bytes_to_kzg_proof(aggregated_proof_bytes)
|
||||
return verify_kzg_proof_impl(aggregated_poly_commitment, evaluation_challenge, y, aggregated_proof)
|
||||
```
|
||||
|
||||
@@ -43,11 +43,12 @@ This document is the beacon chain fork choice spec, part of Phase 0. It assumes
|
||||
|
||||
## Fork choice
|
||||
|
||||
The head block root associated with a `store` is defined as `get_head(store)`. At genesis, let `store = get_forkchoice_store(genesis_state)` and update `store` by running:
|
||||
The head block root associated with a `store` is defined as `get_head(store)`. At genesis, let `store = get_forkchoice_store(genesis_state, genesis_block)` and update `store` by running:
|
||||
|
||||
- `on_tick(store, time)` whenever `time > store.time` where `time` is the current Unix time
|
||||
- `on_block(store, block)` whenever a block `block: SignedBeaconBlock` is received
|
||||
- `on_attestation(store, attestation)` whenever an attestation `attestation` is received
|
||||
- `on_attester_slashing(store, attester_slashing)` whenever an attester slashing `attester_slashing` is received
|
||||
|
||||
Any of the above handlers that trigger an unhandled exception (e.g. a failed assert or an out-of-range list access) are considered invalid. Invalid calls to handlers must not modify `store`.
|
||||
|
||||
@@ -485,4 +486,4 @@ def on_attester_slashing(store: Store, attester_slashing: AttesterSlashing) -> N
|
||||
indices = set(attestation_1.attesting_indices).intersection(attestation_2.attesting_indices)
|
||||
for index in indices:
|
||||
store.equivocating_indices.add(index)
|
||||
```
|
||||
```
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.3.0-rc.1
|
||||
1.3.0-rc.2
|
||||
|
||||
@@ -147,12 +147,14 @@ def test_success_one_partial_withdrawal(spec, state):
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_success_max_per_slot(spec, state):
|
||||
def test_success_mixed_fully_and_partial_withdrawable(spec, state):
|
||||
num_full_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD // 2
|
||||
num_partial_withdrawals = spec.MAX_WITHDRAWALS_PER_PAYLOAD - num_full_withdrawals
|
||||
fully_withdrawable_indices, partial_withdrawals_indices = prepare_expected_withdrawals(
|
||||
spec, state,
|
||||
num_full_withdrawals=num_full_withdrawals, num_partial_withdrawals=num_partial_withdrawals)
|
||||
num_full_withdrawals=num_full_withdrawals,
|
||||
num_partial_withdrawals=num_partial_withdrawals,
|
||||
)
|
||||
|
||||
next_slot(spec, state)
|
||||
execution_payload = build_empty_execution_payload(spec, state)
|
||||
|
||||
@@ -1,22 +1,33 @@
|
||||
from eth2spec.test.helpers.constants import MINIMAL
|
||||
from eth2spec.test.context import (
|
||||
with_capella_and_later, spec_state_test
|
||||
with_capella_and_later,
|
||||
spec_state_test,
|
||||
with_presets,
|
||||
)
|
||||
from eth2spec.test.helpers.keys import pubkeys
|
||||
from eth2spec.test.helpers.state import (
|
||||
next_epoch_via_block,
|
||||
state_transition_and_sign_block,
|
||||
transition_to,
|
||||
next_slot,
|
||||
)
|
||||
from eth2spec.test.helpers.block import (
|
||||
build_empty_block_for_next_slot,
|
||||
build_empty_block,
|
||||
)
|
||||
from eth2spec.test.helpers.bls_to_execution_changes import get_signed_address_change
|
||||
from eth2spec.test.helpers.state import (
|
||||
next_slot,
|
||||
from eth2spec.test.helpers.attestations import (
|
||||
next_epoch_with_attestations,
|
||||
)
|
||||
from eth2spec.test.helpers.withdrawals import (
|
||||
set_eth1_withdrawal_credential_with_balance,
|
||||
set_validator_fully_withdrawable,
|
||||
set_validator_partially_withdrawable,
|
||||
prepare_expected_withdrawals,
|
||||
)
|
||||
from eth2spec.test.helpers.deposits import (
|
||||
prepare_state_and_deposit,
|
||||
)
|
||||
from eth2spec.test.helpers.voluntary_exits import prepare_signed_exits
|
||||
|
||||
|
||||
@@ -255,3 +266,156 @@ def test_invalid_withdrawal_fail_second_block_payload_isnt_compatible(spec, stat
|
||||
|
||||
yield 'blocks', [signed_block_2]
|
||||
yield 'post', None
|
||||
|
||||
|
||||
#
|
||||
# Mix top-ups and withdrawals
|
||||
#
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_top_up_and_partial_withdrawable_validator(spec, state):
|
||||
next_withdrawal_validator_index = 0
|
||||
validator_index = next_withdrawal_validator_index + 1
|
||||
|
||||
set_eth1_withdrawal_credential_with_balance(spec, state, validator_index, spec.MAX_EFFECTIVE_BALANCE)
|
||||
validator = state.validators[validator_index]
|
||||
balance = state.balances[validator_index]
|
||||
assert not spec.is_partially_withdrawable_validator(validator, balance)
|
||||
|
||||
# Make a top-up balance to validator
|
||||
amount = spec.MAX_EFFECTIVE_BALANCE // 4
|
||||
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.body.deposits.append(deposit)
|
||||
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
# Since withdrawals happen before deposits, it becomes partially withdrawable after state transition.
|
||||
validator = state.validators[validator_index]
|
||||
balance = state.balances[validator_index]
|
||||
assert spec.is_partially_withdrawable_validator(validator, balance)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@spec_state_test
|
||||
def test_top_up_to_fully_withdrawn_validator(spec, state):
|
||||
"""
|
||||
Similar to `teste_process_deposit::test_success_top_up_to_withdrawn_validator` test.
|
||||
"""
|
||||
next_withdrawal_validator_index = 0
|
||||
validator_index = next_withdrawal_validator_index + 1
|
||||
|
||||
# Fully withdraw validator
|
||||
set_validator_fully_withdrawable(spec, state, validator_index)
|
||||
assert state.balances[validator_index] > 0
|
||||
next_epoch_via_block(spec, state)
|
||||
assert state.balances[validator_index] == 0
|
||||
assert state.validators[validator_index].effective_balance > 0
|
||||
next_epoch_via_block(spec, state)
|
||||
assert state.validators[validator_index].effective_balance == 0
|
||||
|
||||
# Make a top-up deposit to validator
|
||||
amount = spec.MAX_EFFECTIVE_BALANCE // 4
|
||||
deposit = prepare_state_and_deposit(spec, state, validator_index, amount, signed=True)
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.body.deposits.append(deposit)
|
||||
|
||||
signed_block_1 = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
assert spec.is_fully_withdrawable_validator(
|
||||
state.validators[validator_index],
|
||||
state.balances[validator_index],
|
||||
spec.get_current_epoch(state)
|
||||
)
|
||||
|
||||
# Apply an empty block
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
signed_block_2 = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
# With mainnet preset, it holds
|
||||
if len(state.validators) <= spec.MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP:
|
||||
assert not spec.is_fully_withdrawable_validator(
|
||||
state.validators[validator_index],
|
||||
state.balances[validator_index],
|
||||
spec.get_current_epoch(state)
|
||||
)
|
||||
|
||||
yield 'blocks', [signed_block_1, signed_block_2]
|
||||
yield 'post', state
|
||||
|
||||
|
||||
def _insert_validator(spec, state, balance):
|
||||
effective_balance = balance if balance < spec.MAX_EFFECTIVE_BALANCE else spec.MAX_EFFECTIVE_BALANCE
|
||||
validator_index = len(state.validators)
|
||||
validator = spec.Validator(
|
||||
pubkey=pubkeys[validator_index],
|
||||
withdrawal_credentials=spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX + b'\x00' * 11 + b'\x56' * 20,
|
||||
activation_eligibility_epoch=1,
|
||||
activation_epoch=2,
|
||||
exit_epoch=spec.FAR_FUTURE_EPOCH,
|
||||
withdrawable_epoch=spec.FAR_FUTURE_EPOCH,
|
||||
effective_balance=effective_balance,
|
||||
)
|
||||
state.validators.append(validator)
|
||||
state.balances.append(balance)
|
||||
state.previous_epoch_participation.append(spec.ParticipationFlags(0b0000_0000))
|
||||
state.current_epoch_participation.append(spec.ParticipationFlags(0b0000_0000))
|
||||
state.inactivity_scores.append(0)
|
||||
|
||||
return validator_index
|
||||
|
||||
|
||||
def _run_activate_and_partial_withdrawal(spec, state, initial_balance):
|
||||
validator_index = _insert_validator(spec, state, balance=initial_balance)
|
||||
|
||||
# To make it eligibile activation
|
||||
transition_to(spec, state, spec.compute_start_slot_at_epoch(2) - 1)
|
||||
assert not spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
blocks = []
|
||||
# To activate
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
blocks.append(signed_block)
|
||||
|
||||
assert spec.is_active_validator(state.validators[validator_index], spec.get_current_epoch(state))
|
||||
|
||||
if initial_balance > spec.MAX_EFFECTIVE_BALANCE:
|
||||
assert spec.is_partially_withdrawable_validator(
|
||||
state.validators[validator_index], state.balances[validator_index])
|
||||
else:
|
||||
assert not spec.is_partially_withdrawable_validator(
|
||||
state.validators[validator_index], state.balances[validator_index])
|
||||
|
||||
_, new_blocks, state = next_epoch_with_attestations(spec, state, True, True)
|
||||
blocks += new_blocks
|
||||
|
||||
yield 'blocks', blocks
|
||||
yield 'post', state
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_presets([MINIMAL], reason="too many validators with mainnet config")
|
||||
@spec_state_test
|
||||
def test_activate_and_partial_withdrawal_max_effective_balance(spec, state):
|
||||
yield from _run_activate_and_partial_withdrawal(spec, state, initial_balance=spec.MAX_EFFECTIVE_BALANCE)
|
||||
|
||||
|
||||
@with_capella_and_later
|
||||
@with_presets([MINIMAL], reason="too many validators with mainnet config")
|
||||
@spec_state_test
|
||||
def test_activate_and_partial_withdrawal_overdeposit(spec, state):
|
||||
yield from _run_activate_and_partial_withdrawal(spec, state, initial_balance=spec.MAX_EFFECTIVE_BALANCE + 10000000)
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
from eth2spec.test.context import (
|
||||
ForkMeta,
|
||||
always_bls,
|
||||
with_fork_metas,
|
||||
)
|
||||
from eth2spec.test.helpers.constants import (
|
||||
AFTER_DENEB_PRE_POST_FORKS,
|
||||
)
|
||||
from eth2spec.test.helpers.fork_transition import (
|
||||
OperationType,
|
||||
run_transition_with_operation,
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# BLSToExecutionChange
|
||||
#
|
||||
|
||||
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2)
|
||||
for pre, post in AFTER_DENEB_PRE_POST_FORKS])
|
||||
@always_bls
|
||||
def test_transition_with_btec_right_after_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
|
||||
"""
|
||||
Create a BLS_TO_EXECUTION_CHANGE right *after* the transition
|
||||
"""
|
||||
yield from run_transition_with_operation(
|
||||
state,
|
||||
fork_epoch,
|
||||
spec,
|
||||
post_spec,
|
||||
pre_tag,
|
||||
post_tag,
|
||||
operation_type=OperationType.BLS_TO_EXECUTION_CHANGE,
|
||||
operation_at_slot=fork_epoch * spec.SLOTS_PER_EPOCH,
|
||||
)
|
||||
|
||||
|
||||
@with_fork_metas([ForkMeta(pre_fork_name=pre, post_fork_name=post, fork_epoch=2)
|
||||
for pre, post in AFTER_DENEB_PRE_POST_FORKS])
|
||||
@always_bls
|
||||
def test_transition_with_btec_right_before_fork(state, fork_epoch, spec, post_spec, pre_tag, post_tag):
|
||||
"""
|
||||
Create a BLS_TO_EXECUTION_CHANGE right *before* the transition
|
||||
"""
|
||||
yield from run_transition_with_operation(
|
||||
state,
|
||||
fork_epoch,
|
||||
spec,
|
||||
post_spec,
|
||||
pre_tag,
|
||||
post_tag,
|
||||
operation_type=OperationType.BLS_TO_EXECUTION_CHANGE,
|
||||
operation_at_slot=fork_epoch * spec.SLOTS_PER_EPOCH - 1,
|
||||
)
|
||||
@@ -18,7 +18,7 @@ def test_verify_kzg_proof(spec, state):
|
||||
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)
|
||||
proof = spec.compute_kzg_proof_impl(polynomial, x)
|
||||
|
||||
y = spec.evaluate_polynomial_in_evaluation_form(polynomial, x)
|
||||
assert spec.verify_kzg_proof_impl(commitment, x, y, proof)
|
||||
|
||||
@@ -37,6 +37,12 @@ ALL_FORK_UPGRADES = {
|
||||
ALL_PRE_POST_FORKS = ALL_FORK_UPGRADES.items()
|
||||
AFTER_BELLATRIX_UPGRADES = {key: value for key, value in ALL_FORK_UPGRADES.items() if key != PHASE0}
|
||||
AFTER_BELLATRIX_PRE_POST_FORKS = AFTER_BELLATRIX_UPGRADES.items()
|
||||
AFTER_CAPELLA_UPGRADES = {key: value for key, value in ALL_FORK_UPGRADES.items()
|
||||
if key not in [PHASE0, ALTAIR]}
|
||||
AFTER_CAPELLA_PRE_POST_FORKS = AFTER_CAPELLA_UPGRADES.items()
|
||||
AFTER_DENEB_UPGRADES = {key: value for key, value in ALL_FORK_UPGRADES.items()
|
||||
if key not in [PHASE0, ALTAIR, BELLATRIX]}
|
||||
AFTER_DENEB_PRE_POST_FORKS = AFTER_DENEB_UPGRADES.items()
|
||||
|
||||
#
|
||||
# Config
|
||||
|
||||
@@ -9,6 +9,7 @@ from eth2spec.test.helpers.block import (
|
||||
build_empty_block,
|
||||
sign_block,
|
||||
)
|
||||
from eth2spec.test.helpers.bls_to_execution_changes import get_signed_address_change
|
||||
from eth2spec.test.helpers.constants import (
|
||||
ALTAIR,
|
||||
BELLATRIX,
|
||||
@@ -36,6 +37,7 @@ class OperationType(Enum):
|
||||
ATTESTER_SLASHING = auto()
|
||||
DEPOSIT = auto()
|
||||
VOLUNTARY_EXIT = auto()
|
||||
BLS_TO_EXECUTION_CHANGE = auto()
|
||||
|
||||
|
||||
def _set_operations_by_dict(block, operation_dict):
|
||||
@@ -267,6 +269,10 @@ def run_transition_with_operation(state,
|
||||
selected_validator_index = 0
|
||||
signed_exits = prepare_signed_exits(spec, state, [selected_validator_index])
|
||||
operation_dict = {'voluntary_exits': signed_exits}
|
||||
elif operation_type == OperationType.BLS_TO_EXECUTION_CHANGE:
|
||||
selected_validator_index = 0
|
||||
bls_to_execution_changes = [get_signed_address_change(spec, state, selected_validator_index)]
|
||||
operation_dict = {'bls_to_execution_changes': bls_to_execution_changes}
|
||||
|
||||
def _check_state():
|
||||
if operation_type == OperationType.PROPOSER_SLASHING:
|
||||
@@ -288,6 +294,9 @@ def run_transition_with_operation(state,
|
||||
elif operation_type == OperationType.VOLUNTARY_EXIT:
|
||||
validator = state.validators[selected_validator_index]
|
||||
assert validator.exit_epoch < post_spec.FAR_FUTURE_EPOCH
|
||||
elif operation_type == OperationType.BLS_TO_EXECUTION_CHANGE:
|
||||
validator = state.validators[selected_validator_index]
|
||||
assert validator.withdrawal_credentials[:1] == spec.ETH1_ADDRESS_WITHDRAWAL_PREFIX
|
||||
|
||||
yield "pre", state
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@ from eth2spec.test.altair.transition import (
|
||||
test_slashing as test_altair_slashing,
|
||||
test_operations as test_altair_operations,
|
||||
)
|
||||
from eth2spec.test.eip4844.transition import (
|
||||
test_operations as test_eip4844_operations,
|
||||
)
|
||||
|
||||
|
||||
def create_provider(tests_src, preset_name: str, pre_fork_name: str, post_fork_name: str) -> gen_typing.TestProvider:
|
||||
@@ -37,14 +40,14 @@ def create_provider(tests_src, preset_name: str, pre_fork_name: str, post_fork_n
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
altair_tests = (
|
||||
all_tests = (
|
||||
test_altair_transition,
|
||||
test_altair_activations_and_exits,
|
||||
test_altair_leaking,
|
||||
test_altair_slashing,
|
||||
test_altair_operations,
|
||||
test_eip4844_operations,
|
||||
)
|
||||
all_tests = altair_tests
|
||||
for transition_test_module in all_tests:
|
||||
for pre_fork, post_fork in ALL_PRE_POST_FORKS:
|
||||
gen_runner.run_generator("transition", [
|
||||
|
||||
Reference in New Issue
Block a user