diff --git a/specs/deneb/p2p-interface.md b/specs/deneb/p2p-interface.md index 5e21bc542..852597b09 100644 --- a/specs/deneb/p2p-interface.md +++ b/specs/deneb/p2p-interface.md @@ -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`. diff --git a/specs/deneb/polynomial-commitments.md b/specs/deneb/polynomial-commitments.md index 26ee00487..9a0500d96 100644 --- a/specs/deneb/polynomial-commitments.md +++ b/specs/deneb/polynomial-commitments.md @@ -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) ``` diff --git a/specs/phase0/fork-choice.md b/specs/phase0/fork-choice.md index 661ad613b..e535184af 100644 --- a/specs/phase0/fork-choice.md +++ b/specs/phase0/fork-choice.md @@ -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) -``` \ No newline at end of file +``` diff --git a/tests/core/pyspec/eth2spec/VERSION.txt b/tests/core/pyspec/eth2spec/VERSION.txt index bf16dded0..1d074f43e 100644 --- a/tests/core/pyspec/eth2spec/VERSION.txt +++ b/tests/core/pyspec/eth2spec/VERSION.txt @@ -1 +1 @@ -1.3.0-rc.1 +1.3.0-rc.2 diff --git a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py index 674231096..d7813fb1f 100644 --- a/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py +++ b/tests/core/pyspec/eth2spec/test/capella/block_processing/test_process_withdrawals.py @@ -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) diff --git a/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py index 1cd1c1317..079990e3e 100644 --- a/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/capella/sanity/test_blocks.py @@ -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) diff --git a/tests/core/pyspec/eth2spec/test/deneb/transition/__init__.py b/tests/core/pyspec/eth2spec/test/deneb/transition/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/core/pyspec/eth2spec/test/deneb/transition/test_operations.py b/tests/core/pyspec/eth2spec/test/deneb/transition/test_operations.py new file mode 100644 index 000000000..f945afa8f --- /dev/null +++ b/tests/core/pyspec/eth2spec/test/deneb/transition/test_operations.py @@ -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, + ) diff --git a/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py b/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py index f60c79a88..4d881e3e3 100644 --- a/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py +++ b/tests/core/pyspec/eth2spec/test/deneb/unittests/polynomial_commitments/test_polynomial_commitments.py @@ -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) diff --git a/tests/core/pyspec/eth2spec/test/helpers/constants.py b/tests/core/pyspec/eth2spec/test/helpers/constants.py index 2c92b1c7e..0d31adb43 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/constants.py +++ b/tests/core/pyspec/eth2spec/test/helpers/constants.py @@ -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 diff --git a/tests/core/pyspec/eth2spec/test/helpers/fork_transition.py b/tests/core/pyspec/eth2spec/test/helpers/fork_transition.py index 52d1bc67e..96d0d20dc 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/fork_transition.py +++ b/tests/core/pyspec/eth2spec/test/helpers/fork_transition.py @@ -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 diff --git a/tests/generators/transition/main.py b/tests/generators/transition/main.py index 7de7213bd..a4eba90df 100644 --- a/tests/generators/transition/main.py +++ b/tests/generators/transition/main.py @@ -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", [