Merge remote-tracking branch 'origin/dev' into single-attestation

This commit is contained in:
Jacek Sieka 2024-09-20 09:28:51 +02:00
commit 9bef69c4b5
No known key found for this signature in database
GPG Key ID: A1B09461ABB656B8
57 changed files with 501 additions and 428 deletions

View File

@ -12,17 +12,19 @@ on:
default: dev
type: string
required: true
schedule:
- cron: '0 2 * * *'
jobs:
generate-tests:
runs-on: [self-hosted-ghr-custom, size-chungus-x64, profile-consensusSpecs]
runs-on: [self-hosted-ghr-custom, size-xl-x64, profile-consensusSpecs]
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
repository: 'ethereum/consensus-specs'
path: 'consensus-specs'
ref: ${{ inputs.source_ref }}
ref: ${{ inputs.ref || 'dev' }}
- name: Checkout consensus-spec-tests repository
uses: actions/checkout@v4
with:

View File

@ -96,6 +96,9 @@ dist_check:
dist_upload:
python3 -m twine upload dist/*
build_wheel: install_test pyspec
. venv/bin/activate && \
python3 -m build --no-isolation --outdir ./dist ./
# "make generate_tests" to run all generators
generate_tests: $(GENERATOR_TARGETS)
@ -195,7 +198,8 @@ define run_generator
cd $(GENERATOR_DIR)/$(1); \
if ! test -d venv; then python3 -m venv venv; fi; \
. venv/bin/activate; \
pip3 install -r requirements.txt; \
pip3 install ../../../dist/eth2spec-*.whl; \
pip3 install 'eth2spec[generator]'; \
python3 main.py -o $(CURRENT_DIR)/$(TEST_VECTOR_DIR); \
echo "generator $(1) finished"
endef
@ -217,7 +221,7 @@ gen_kzg_setups:
# For any generator, build it using the run_generator function.
# (creation of output dir is a dependency)
gen_%: $(TEST_VECTOR_DIR)
gen_%: build_wheel $(TEST_VECTOR_DIR)
$(call run_generator,$*)
detect_generator_incomplete: $(TEST_VECTOR_DIR)

View File

@ -73,3 +73,19 @@ Documentation on the different components used during spec writing can be found
## Consensus spec tests
Conformance tests built from the executable python spec are available in the [Ethereum Proof-of-Stake Consensus Spec Tests](https://github.com/ethereum/consensus-spec-tests) repo. Compressed tarballs are available in [releases](https://github.com/ethereum/consensus-spec-tests/releases).
## Installation and Usage
The consensus-specs repo can be used by running the tests locally or inside a docker container.
To run the tests locally:
- Clone the repository with `git clone https://github.com/ethereum/consensus-specs.git`
- Switch to the directory `cd consensus-specs`
- Install the dependencies with: `make install_test && make preinstallation && make pyspec`
- Run the tests with `make citest`
To run the tests inside a docker container:
- Switch to the directory with `cd scripts`
- Run the script `./build_run_docker_tests.sh`
- Find the results in a folder called `./testResults`
- Find more ways to customize the script with `./build_run_docker_tests.sh --h`

View File

@ -169,3 +169,6 @@ CUSTODY_REQUIREMENT: 4
# [New in Electra:EIP7251]
MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA: 128000000000 # 2**7 * 10**9 (= 128,000,000,000)
MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT: 256000000000 # 2**8 * 10**9 (= 256,000,000,000)
# EIP7732
MAX_REQUEST_PAYLOADS: 128

View File

@ -168,3 +168,6 @@ CUSTODY_REQUIREMENT: 4
# [New in Electra:EIP7251]
MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA: 64000000000 # 2**6 * 10**9 (= 64,000,000,000)
MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT: 128000000000 # 2**7 * 10**9 (= 128,000,000,000)
# EIP7732
MAX_REQUEST_PAYLOADS: 128

View File

@ -13,8 +13,8 @@ Ideally manual running of docker containers is for advanced users, we recommend
The `scripts/build_run_docker_tests.sh` script will cover most usecases. The script allows the user to configure the fork(altair/bellatrix/capella..), `$IMAGE_NAME` (specifies the container to use), preset type (mainnet/minimal), and test all forks flags. Ideally, this is the main way that users interact with the spec tests instead of running it locally with varying versions of dependencies.
E.g:
- `./build_run_test.sh --p mainnet` will run the mainnet preset tests
- `./build_run_test.sh --a` will run all the tests across all the forks
- `./build_run_test.sh --f deneb` will only run deneb tests
- `./build_run_docker_tests.sh --p mainnet` will run the mainnet preset tests
- `./build_run_docker_tests.sh --a` will run all the tests across all the forks
- `./build_run_docker_tests.sh --f deneb` will only run deneb tests
Results are always placed in a folder called `./testResults`. The results are `.xml` files and contain the fork they represent and the date/time they were run at.

View File

@ -19,3 +19,41 @@ from eth2spec.deneb import {preset_name} as deneb
'CURRENT_SYNC_COMMITTEE_GINDEX_ELECTRA': 'GeneralizedIndex(86)',
'NEXT_SYNC_COMMITTEE_GINDEX_ELECTRA': 'GeneralizedIndex(87)',
}
@classmethod
def execution_engine_cls(cls) -> str:
return """
class NoopExecutionEngine(ExecutionEngine):
def notify_new_payload(self: ExecutionEngine,
execution_payload: ExecutionPayload,
execution_requests: ExecutionRequests,
parent_beacon_block_root: Root) -> bool:
return True
def notify_forkchoice_updated(self: ExecutionEngine,
head_block_hash: Hash32,
safe_block_hash: Hash32,
finalized_block_hash: Hash32,
payload_attributes: Optional[PayloadAttributes]) -> Optional[PayloadId]:
pass
def get_payload(self: ExecutionEngine, payload_id: PayloadId) -> GetPayloadResponse:
# pylint: disable=unused-argument
raise NotImplementedError("no default block production")
def is_valid_block_hash(self: ExecutionEngine,
execution_payload: ExecutionPayload,
parent_beacon_block_root: Root) -> bool:
return True
def is_valid_versioned_hashes(self: ExecutionEngine, new_payload_request: NewPayloadRequest) -> bool:
return True
def verify_and_notify_new_payload(self: ExecutionEngine,
new_payload_request: NewPayloadRequest) -> bool:
return True
EXECUTION_ENGINE = NoopExecutionEngine()"""

View File

@ -2,3 +2,4 @@ pip>=24.0.0
wheel>=0.44.0
setuptools>=72.0.0
pylint>=3.2.0
build>=1.2.2

View File

@ -10,7 +10,7 @@
# Set variables
ALL_EXECUTABLE_SPECS=("phase0" "altair" "bellatrix" "capella" "deneb" "electra" "whisk")
ALL_EXECUTABLE_SPECS=("phase0" "altair" "bellatrix" "capella" "deneb" "electra" "whisk" "eip7594")
TEST_PRESET_TYPE=minimal
FORK_TO_TEST=phase0
WORKDIR="//consensus-specs//tests//core//pyspec"

View File

@ -524,32 +524,36 @@ setup(
long_description=readme,
long_description_content_type="text/markdown",
author="ethereum",
url="https://github.com/ethereum/eth2.0-specs",
url="https://github.com/ethereum/consensus-specs",
include_package_data=False,
package_data={'configs': ['*.yaml'],
'presets': ['*.yaml'],
package_data={
'configs': ['*.yaml'],
'eth2spec': ['VERSION.txt'],
'presets': ['**/*.yaml', '**/*.json'],
'specs': ['**/*.md'],
'eth2spec': ['VERSION.txt']},
'sync': ['optimistic.md'],
},
package_dir={
"eth2spec": "tests/core/pyspec/eth2spec",
"configs": "configs",
"eth2spec": "tests/core/pyspec/eth2spec",
"presets": "presets",
"specs": "specs",
"sync": "sync",
},
packages=find_packages(where='tests/core/pyspec') + ['configs', 'specs'],
packages=find_packages(where='tests/core/pyspec') + ['configs', 'presets', 'specs', 'presets', 'sync'],
py_modules=["eth2spec"],
cmdclass=commands,
python_requires=">=3.9, <4",
extras_require={
"test": ["pytest>=4.4", "pytest-cov", "pytest-xdist"],
"lint": ["flake8==5.0.4", "mypy==0.981", "pylint==2.15.3"],
"generator": ["python-snappy==0.6.1", "filelock", "pathos==0.3.0"],
"generator": ["setuptools>=72.0.0", "pytest>4.4", "python-snappy==0.7.3", "filelock", "pathos==0.3.0"],
"docs": ["mkdocs==1.4.2", "mkdocs-material==9.1.5", "mdx-truly-sane-lists==1.3", "mkdocs-awesome-pages-plugin==2.8.0"]
},
install_requires=[
"eth-utils>=2.0.0,<3",
"eth-typing>=3.2.0,<4.0.0",
"pycryptodome==3.15.0",
"pycryptodome>=3.19.1",
"py_ecc==6.0.0",
"milagro_bls_binding==1.9.0",
"remerkleable==0.1.28",

View File

@ -67,7 +67,7 @@ The following values are (non-configurable) constants used throughout the specif
| Name | Value | Description |
| - | - | - |
| `DATA_COLUMN_SIDECAR_SUBNET_COUNT` | `128` | The number of data column sidecar subnets used in the gossipsub protocol |
| `DATA_COLUMN_SIDECAR_SUBNET_COUNT` | `uint64(128)` | The number of data column sidecar subnets used in the gossipsub protocol |
### Custody setting
@ -222,7 +222,7 @@ def get_data_column_sidecars(signed_block: SignedBeaconBlock,
Each node downloads and custodies a minimum of `CUSTODY_REQUIREMENT` subnets per slot. The particular subnets that the node is required to custody are selected pseudo-randomly (more on this below).
A node *may* choose to custody and serve more than the minimum honesty requirement. Such a node explicitly advertises a number greater than `CUSTODY_REQUIREMENT` via the peer discovery mechanism -- for example, in their ENR (e.g. `custody_subnet_count: 4` if the node custodies `4` subnets each slot) -- up to a `DATA_COLUMN_SIDECAR_SUBNET_COUNT` (i.e. a super-full node).
A node *may* choose to custody and serve more than the minimum honesty requirement. Such a node explicitly advertises a number greater than `CUSTODY_REQUIREMENT` through the peer discovery mechanism, specifically by setting a higher value in the `custody_subnet_count` field within its ENR. This value can be increased up to `DATA_COLUMN_SIDECAR_SUBNET_COUNT`, indicating a super-full node.
A node stores the custodied columns for the duration of the pruning period and responds to peer requests for samples on those columns.

View File

@ -119,14 +119,14 @@ The `MetaData` stored locally by clients is updated with an additional field to
seq_number: uint64
attnets: Bitvector[ATTESTATION_SUBNET_COUNT]
syncnets: Bitvector[SYNC_COMMITTEE_SUBNET_COUNT]
custody_subnet_count: uint64
custody_subnet_count: uint64 # csc
)
```
Where
- `seq_number`, `attnets`, and `syncnets` have the same meaning defined in the Altair document.
- `custody_subnet_count` represents the node's custody subnet count. Clients MAY reject ENRs with a value less than `CUSTODY_REQUIREMENT`.
- `custody_subnet_count` represents the node's custody subnet count. Clients MAY reject peers with a value less than `CUSTODY_REQUIREMENT`.
### The gossip domain: gossipsub
@ -324,4 +324,4 @@ A new field is added to the ENR under the key `csc` to facilitate custody data c
| Key | Value |
|--------|------------------------------------------|
| `csc` | Custody subnet count, big endian integer |
| `csc` | Custody subnet count, `uint64` big endian integer with no leading zero bytes (`0` is encoded as empty byte string) |

View File

@ -15,8 +15,6 @@
- [BLS12-381 helpers](#bls12-381-helpers)
- [`cell_to_coset_evals`](#cell_to_coset_evals)
- [`coset_evals_to_cell`](#coset_evals_to_cell)
- [Linear combinations](#linear-combinations)
- [`g2_lincomb`](#g2_lincomb)
- [FFTs](#ffts)
- [`_fft_field`](#_fft_field)
- [`fft_field`](#fft_field)
@ -125,28 +123,6 @@ def coset_evals_to_cell(coset_evals: CosetEvals) -> Cell:
return Cell(cell)
```
### Linear combinations
#### `g2_lincomb`
```python
def g2_lincomb(points: Sequence[G2Point], scalars: Sequence[BLSFieldElement]) -> Bytes96:
"""
BLS multiscalar multiplication in G2. This can be naively implemented using double-and-add.
"""
assert len(points) == len(scalars)
if len(points) == 0:
return bls.G2_to_bytes96(bls.Z2())
points_g2 = []
for point in points:
points_g2.append(bls.bytes96_to_G2(point))
result = bls.multi_exp(points_g2, scalars)
return Bytes96(bls.G2_to_bytes96(result))
```
### FFTs
#### `_fft_field`

View File

@ -50,10 +50,11 @@
- [Modified `process_operations`](#modified-process_operations)
- [Payload Attestations](#payload-attestations)
- [`process_payload_attestation`](#process_payload_attestation)
- [Modified `process_execution_payload`](#modified-process_execution_payload)
- [New `verify_execution_payload_envelope_signature`](#new-verify_execution_payload_envelope_signature)
- [Modified `is_merge_transition_complete`](#modified-is_merge_transition_complete)
- [Modified `validate_merge_block`](#modified-validate_merge_block)
- [Execution payload processing](#execution-payload-processing)
- [New `verify_execution_payload_envelope_signature`](#new-verify_execution_payload_envelope_signature)
- [New `process_execution_payload`](#new-process_execution_payload)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
@ -156,6 +157,7 @@ class SignedExecutionPayloadHeader(Container):
```python
class ExecutionPayloadEnvelope(Container):
payload: ExecutionPayload
execution_requests: ExecutionRequests
builder_index: ValidatorIndex
beacon_block_root: Root
blob_kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]
@ -175,7 +177,7 @@ class SignedExecutionPayloadEnvelope(Container):
#### `BeaconBlockBody`
**Note:** The Beacon Block body is modified to contain a `Signed ExecutionPayloadHeader`. The containers `BeaconBlock` and `SignedBeaconBlock` are modified indirectly.
**Note:** The Beacon Block body is modified to contain a `Signed ExecutionPayloadHeader`. The containers `BeaconBlock` and `SignedBeaconBlock` are modified indirectly. The field `execution_requests` is removed from the beacon block body and moved into the signed execution payload envelope.
```python
class BeaconBlockBody(Container):
@ -184,15 +186,16 @@ class BeaconBlockBody(Container):
graffiti: Bytes32 # Arbitrary data
# Operations
proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS]
attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS]
attestations: List[Attestation, MAX_ATTESTATIONS]
attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS_ELECTRA]
attestations: List[Attestation, MAX_ATTESTATIONS_ELECTRA]
deposits: List[Deposit, MAX_DEPOSITS]
voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS]
sync_aggregate: SyncAggregate
# Execution
# Removed execution_payload [Removed in EIP-7732]
# Removed blob_kzg_commitments [Removed in EIP-7732]
bls_to_execution_changes: List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES]
# Removed blob_kzg_commitments [Removed in EIP-7732]
# Removed execution_requests [Removed in EIP-7732]
# PBS
signed_execution_payload_header: SignedExecutionPayloadHeader # [New in EIP-7732]
payload_attestations: List[PayloadAttestation, MAX_PAYLOAD_ATTESTATIONS] # [New in EIP-7732]
@ -427,7 +430,8 @@ The post-state corresponding to a pre-state `state` and a signed execution paylo
def process_block(state: BeaconState, block: BeaconBlock) -> None:
process_block_header(state, block)
process_withdrawals(state) # [Modified in EIP-7732]
process_execution_payload_header(state, block) # [Modified in EIP-7732, removed process_execution_payload]
# Removed `process_execution_payload` in EIP-7732
process_execution_payload_header(state, block) # [New in EIP-7732]
process_randao(state, block.body)
process_eth1_data(state, block.body)
process_operations(state, block.body) # [Modified in EIP-7732]
@ -493,9 +497,12 @@ def process_execution_payload_header(state: BeaconState, block: BeaconBlock) ->
signed_header = block.body.signed_execution_payload_header
assert verify_execution_payload_header_signature(state, signed_header)
# Check that the builder has funds to cover the bid
# Check that the builder is active non-slashed has funds to cover the bid
header = signed_header.message
builder_index = header.builder_index
builder = state.validators[builder_index]
assert is_active_validator(builder, get_current_epoch(state))
assert not builder.slashed
amount = header.value
assert state.balances[builder_index] >= amount
@ -592,9 +599,51 @@ def process_payload_attestation(state: BeaconState, payload_attestation: Payload
increase_balance(state, proposer_index, proposer_reward)
```
#### Modified `process_execution_payload`
#### Modified `is_merge_transition_complete`
##### New `verify_execution_payload_envelope_signature`
`is_merge_transition_complete` is modified only for testing purposes to add the blob kzg commitments root for an empty list
```python
def is_merge_transition_complete(state: BeaconState) -> bool:
header = ExecutionPayloadHeader()
kzgs = List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]()
header.blob_kzg_commitments_root = kzgs.hash_tree_root()
return state.latest_execution_payload_header != header
```
#### Modified `validate_merge_block`
`validate_merge_block` is modified to use the new `signed_execution_payload_header` message in the Beacon Block Body
```python
def validate_merge_block(block: BeaconBlock) -> None:
"""
Check the parent PoW block of execution payload is a valid terminal PoW block.
Note: Unavailable PoW block(s) may later become available,
and a client software MAY delay a call to ``validate_merge_block``
until the PoW block(s) become available.
"""
if TERMINAL_BLOCK_HASH != Hash32():
# If `TERMINAL_BLOCK_HASH` is used as an override, the activation epoch must be reached.
assert compute_epoch_at_slot(block.slot) >= TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH
assert block.body.signed_execution_payload_header.message.parent_block_hash == TERMINAL_BLOCK_HASH
return
# Modified in EIP-7732
pow_block = get_pow_block(block.body.signed_execution_payload_header.message.parent_block_hash)
# Check if `pow_block` is available
assert pow_block is not None
pow_parent = get_pow_block(pow_block.parent_hash)
# Check if `pow_parent` is available
assert pow_parent is not None
# Check if `pow_block` is a valid terminal PoW block
assert is_valid_terminal_pow_block(pow_block, pow_parent)
```
### Execution payload processing
#### New `verify_execution_payload_envelope_signature`
```python
def verify_execution_payload_envelope_signature(
@ -604,6 +653,8 @@ def verify_execution_payload_envelope_signature(
return bls.Verify(builder.pubkey, signing_root, signed_envelope.signature)
```
#### New `process_execution_payload`
*Note*: `process_execution_payload` is now an independent check in state transition. It is called when importing a signed execution payload proposed by the builder of the current slot.
```python
@ -647,11 +698,13 @@ def process_execution_payload(state: BeaconState,
# Verify the execution payload is valid
versioned_hashes = [kzg_commitment_to_versioned_hash(commitment)
for commitment in envelope.blob_kzg_commitments]
requests = envelope.execution_requests
assert execution_engine.verify_and_notify_new_payload(
NewPayloadRequest(
execution_payload=payload,
versioned_hashes=versioned_hashes,
parent_beacon_block_root=state.latest_block_header.parent_root,
execution_requests=requests,
)
)
@ -660,9 +713,9 @@ def process_execution_payload(state: BeaconState,
for operation in operations:
fn(state, operation)
for_ops(payload.deposit_requests, process_deposit_request)
for_ops(payload.withdrawal_requests, process_withdrawal_request)
for_ops(payload, process_consolidation_request)
for_ops(requests.deposit_requests, process_deposit_request)
for_ops(requests.withdrawal_requests, process_withdrawal_request)
for_ops(requests.consolidation_requests, process_consolidation_request)
# Cache the execution payload header and proposer
state.latest_block_hash = payload.block_hash
@ -672,45 +725,3 @@ def process_execution_payload(state: BeaconState,
if verify:
assert envelope.state_root == hash_tree_root(state)
```
#### Modified `is_merge_transition_complete`
`is_merge_transition_complete` is modified only for testing purposes to add the blob kzg commitments root for an empty list
```python
def is_merge_transition_complete(state: BeaconState) -> bool:
header = ExecutionPayloadHeader()
kzgs = List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]()
header.blob_kzg_commitments_root = kzgs.hash_tree_root()
return state.latest_execution_payload_header != header
```
#### Modified `validate_merge_block`
`validate_merge_block` is modified to use the new `signed_execution_payload_header` message in the Beacon Block Body
```python
def validate_merge_block(block: BeaconBlock) -> None:
"""
Check the parent PoW block of execution payload is a valid terminal PoW block.
Note: Unavailable PoW block(s) may later become available,
and a client software MAY delay a call to ``validate_merge_block``
until the PoW block(s) become available.
"""
if TERMINAL_BLOCK_HASH != Hash32():
# If `TERMINAL_BLOCK_HASH` is used as an override, the activation epoch must be reached.
assert compute_epoch_at_slot(block.slot) >= TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH
assert block.body.signed_execution_payload_header.message.parent_block_hash == TERMINAL_BLOCK_HASH
return
# Modified in EIP-7732
pow_block = get_pow_block(block.body.signed_execution_payload_header.message.parent_block_hash)
# Check if `pow_block` is available
assert pow_block is not None
pow_parent = get_pow_block(pow_block.parent_hash)
# Check if `pow_parent` is available
assert pow_parent is not None
# Check if `pow_block` is a valid terminal PoW block
assert is_valid_terminal_pow_block(pow_block, pow_parent)
```

View File

@ -7,6 +7,7 @@ This document contains the consensus-layer networking specification for EIP7732.
- [Modification in EIP-7732](#modification-in-eip-7732)
- [Preset](#preset)
- [Configuration](#configuration)
- [Containers](#containers)
- [`BlobSidecar`](#blobsidecar)
- [Helpers](#helpers)
@ -23,7 +24,7 @@ This document contains the consensus-layer networking specification for EIP7732.
- [BeaconBlocksByRange v3](#beaconblocksbyrange-v3)
- [BeaconBlocksByRoot v3](#beaconblocksbyroot-v3)
- [BlobSidecarsByRoot v2](#blobsidecarsbyroot-v2)
- [ExecutionPayloadEnvelopeByRoot v1](#executionpayloadenvelopebyroot-v1)
- [ExecutionPayloadEnvelopesByRoot v1](#executionpayloadenvelopesbyroot-v1)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@ -37,6 +38,14 @@ This document contains the consensus-layer networking specification for EIP7732.
|------------------------------------------|-----------------------------------|---------------------------------------------------------------------|
| `KZG_COMMITMENT_INCLUSION_PROOF_DEPTH_EIP7732` | `13` # TODO: Compute it when the spec stabilizes | Merkle proof depth for the `blob_kzg_commitments` list item |
### Configuration
*[New in EIP7732]*
| Name | Value | Description |
|------------------------|----------------|-------------------------------------------------------------------|
| `MAX_REQUEST_PAYLOADS` | `2**7` (= 128) | Maximum number of execution payload envelopes in a single request |
### Containers
@ -170,9 +179,10 @@ The following validations MUST pass before forwarding the `signed_execution_payl
- _[IGNORE]_ this is the first signed bid seen with a valid signature from the given builder for this slot.
- _[IGNORE]_ this bid is the highest value bid seen for the pair of the corresponding slot and the given parent block hash.
- _[REJECT]_ The signed builder bid, `header.builder_index` is a valid and non-slashed builder index in state.
- _[REJECT]_ The signed builder bid, `header.builder_index` is a valid, active, and non-slashed builder index in state.
- _[IGNORE]_ The signed builder bid value, `header.value`, is less or equal than the builder's balance in state. i.e. `MIN_BUILDER_BALANCE + header.value < state.builder_balances[header.builder_index]`.
- _[IGNORE]_ `header.parent_block_hash` is the block hash of a known execution payload in fork choice.
_ _[IGNORE]_ `header.parent_block_root` is the hash tree root of a known beacon block in fork choice.
- _[IGNORE]_ `header.slot` is the current slot or the next slot.
- _[REJECT]_ The builder signature, `signed_execution_payload_header_envelope.signature`, is valid with respect to the `header_envelope.builder_index`.
@ -225,9 +235,9 @@ Per `context = compute_fork_digest(fork_version, genesis_validators_root)`:
| `EIP7732_FORK_VERSION` | `eip7732.BlobSidecar` |
##### ExecutionPayloadEnvelopeByRoot v1
##### ExecutionPayloadEnvelopesByRoot v1
**Protocol ID:** `/eth2/beacon_chain/req/execution_payload_envelope_by_root/1/`
**Protocol ID:** `/eth2/beacon_chain/req/execution_payload_envelopes_by_root/1/`
The `<context-bytes>` field is calculated as `context = compute_fork_digest(fork_version, genesis_validators_root)`:
@ -241,7 +251,7 @@ Request Content:
```
(
List[Root, MAX_REQUEST_PAYLOAD]
List[Root, MAX_REQUEST_PAYLOADS]
)
```
@ -249,14 +259,14 @@ Response Content:
```
(
List[SignedExecutionPayloadEnvelope, MAX_REQUEST_PAYLOAD]
List[SignedExecutionPayloadEnvelope, MAX_REQUEST_PAYLOADS]
)
```
Requests execution payload envelope by `signed_execution_payload_envelope.message.block_root`. The response is a list of `SignedExecutionPayloadEnvelope` whose length is less than or equal to the number of requested execution payload envelopes. It may be less in the case that the responding peer is missing payload envelopes.
Requests execution payload envelopes by `signed_execution_payload_envelope.message.block_root`. The response is a list of `SignedExecutionPayloadEnvelope` whose length is less than or equal to the number of requested execution payload envelopes. It may be less in the case that the responding peer is missing payload envelopes.
No more than `MAX_REQUEST_PAYLOAD` may be requested at a time.
No more than `MAX_REQUEST_PAYLOADS` may be requested at a time.
ExecutionPayloadEnvelopeByRoot is primarily used to recover recent execution payload envelope (e.g. when receiving a payload attestation with revealed status as true but never received a payload).
ExecutionPayloadEnvelopesByRoot is primarily used to recover recent execution payload envelopes (e.g. when receiving a payload attestation with revealed status as true but never received a payload).
The request MUST be encoded as an SSZ-field.

View File

@ -232,7 +232,7 @@ def is_better_update(new_update: LightClientUpdate, old_update: LightClientUpdat
new_has_supermajority = new_num_active_participants * 3 >= max_active_participants * 2
old_has_supermajority = old_num_active_participants * 3 >= max_active_participants * 2
if new_has_supermajority != old_has_supermajority:
return new_has_supermajority > old_has_supermajority
return new_has_supermajority
if not new_has_supermajority and new_num_active_participants != old_num_active_participants:
return new_num_active_participants > old_num_active_participants

View File

@ -12,7 +12,6 @@
- [Constants](#constants)
- [Misc](#misc)
- [Withdrawal prefixes](#withdrawal-prefixes)
- [Domains](#domains)
- [Preset](#preset)
- [Gwei values](#gwei-values)
- [Rewards and penalties](#rewards-and-penalties)
@ -33,12 +32,10 @@
- [`SingleAttestation`](#singleattestation)
- [Modified Containers](#modified-containers)
- [`AttesterSlashing`](#attesterslashing)
- [`BeaconBlockBody`](#beaconblockbody)
- [Extended Containers](#extended-containers)
- [`Attestation`](#attestation)
- [`IndexedAttestation`](#indexedattestation)
- [`BeaconBlockBody`](#beaconblockbody)
- [`ExecutionPayload`](#executionpayload)
- [`ExecutionPayloadHeader`](#executionpayloadheader)
- [`BeaconState`](#beaconstate)
- [Helper functions](#helper-functions)
- [Predicates](#predicates)
@ -51,7 +48,7 @@
- [Modified `is_partially_withdrawable_validator`](#modified-is_partially_withdrawable_validator)
- [Misc](#misc-1)
- [New `get_committee_indices`](#new-get_committee_indices)
- [New `get_validator_max_effective_balance`](#new-get_validator_max_effective_balance)
- [New `get_max_effective_balance`](#new-get_max_effective_balance)
- [Beacon state accessors](#beacon-state-accessors)
- [New `get_balance_churn_limit`](#new-get_balance_churn_limit)
- [New `get_activation_exit_churn_limit`](#new-get_activation_exit_churn_limit)
@ -72,9 +69,16 @@
- [Epoch processing](#epoch-processing)
- [Modified `process_epoch`](#modified-process_epoch)
- [Modified `process_registry_updates`](#modified-process_registry_updates)
- [Modified `process_slashings`](#modified-process_slashings)
- [New `process_pending_balance_deposits`](#new-process_pending_balance_deposits)
- [New `process_pending_consolidations`](#new-process_pending_consolidations)
- [Modified `process_effective_balance_updates`](#modified-process_effective_balance_updates)
- [Execution engine](#execution-engine)
- [Request data](#request-data)
- [Modified `NewPayloadRequest`](#modified-newpayloadrequest)
- [Engine APIs](#engine-apis)
- [Modified `notify_new_payload`](#modified-notify_new_payload)
- [Modified `verify_and_notify_new_payload`](#modified-verify_and_notify_new_payload)
- [Block processing](#block-processing)
- [Withdrawals](#withdrawals)
- [Modified `get_expected_withdrawals`](#modified-get_expected_withdrawals)
@ -130,12 +134,6 @@ The following values are (non-configurable) constants used throughout the specif
| - | - |
| `COMPOUNDING_WITHDRAWAL_PREFIX` | `Bytes1('0x02')` |
### Domains
| Name | Value |
| - | - |
| `DOMAIN_CONSOLIDATION` | `DomainType('0x0B000000')` |
## Preset
### Gwei values
@ -268,6 +266,18 @@ class SingleAttestation(Container):
attester_index: ValidatorIndex
data: AttestationData
signature: BLSSignature
#### `ExecutionRequests`
*Note*: This container holds requests from the execution layer that are received in [
`ExecutionPayloadV4`](https://github.com/ethereum/execution-apis/blob/main/src/engine/prague.md#executionpayloadv4) via
the Engine API. These requests are required for CL state transition (see `BeaconBlockBody`).
```python
class ExecutionRequests(Container):
deposits: List[DepositRequest, MAX_DEPOSIT_REQUESTS_PER_PAYLOAD] # [New in Electra:EIP6110]
withdrawals: List[WithdrawalRequest, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD] # [New in Electra:EIP7002:EIP7251]
consolidations: List[ConsolidationRequest, MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD] # [New in Electra:EIP7251]
```
### Modified Containers
@ -280,6 +290,27 @@ class AttesterSlashing(Container):
attestation_2: IndexedAttestation # [Modified in Electra:EIP7549]
```
#### `BeaconBlockBody`
```python
class BeaconBlockBody(Container):
randao_reveal: BLSSignature
eth1_data: Eth1Data # Eth1 data vote
graffiti: Bytes32 # Arbitrary data
# Operations
proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS]
attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS_ELECTRA] # [Modified in Electra:EIP7549]
attestations: List[Attestation, MAX_ATTESTATIONS_ELECTRA] # [Modified in Electra:EIP7549]
deposits: List[Deposit, MAX_DEPOSITS]
voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS]
sync_aggregate: SyncAggregate
# Execution
execution_payload: ExecutionPayload
bls_to_execution_changes: List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES]
blob_kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]
execution_requests: ExecutionRequests # [New in Electra]
```
### Extended Containers
#### `Attestation`
@ -302,84 +333,6 @@ class IndexedAttestation(Container):
signature: BLSSignature
```
#### `BeaconBlockBody`
```python
class BeaconBlockBody(Container):
randao_reveal: BLSSignature
eth1_data: Eth1Data # Eth1 data vote
graffiti: Bytes32 # Arbitrary data
# Operations
proposer_slashings: List[ProposerSlashing, MAX_PROPOSER_SLASHINGS]
attester_slashings: List[AttesterSlashing, MAX_ATTESTER_SLASHINGS_ELECTRA] # [Modified in Electra:EIP7549]
attestations: List[Attestation, MAX_ATTESTATIONS_ELECTRA] # [Modified in Electra:EIP7549]
deposits: List[Deposit, MAX_DEPOSITS]
voluntary_exits: List[SignedVoluntaryExit, MAX_VOLUNTARY_EXITS]
sync_aggregate: SyncAggregate
# Execution
execution_payload: ExecutionPayload # [Modified in Electra:EIP6110:EIP7002]
bls_to_execution_changes: List[SignedBLSToExecutionChange, MAX_BLS_TO_EXECUTION_CHANGES]
blob_kzg_commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]
```
#### `ExecutionPayload`
```python
class ExecutionPayload(Container):
# Execution block header fields
parent_hash: Hash32
fee_recipient: ExecutionAddress
state_root: Bytes32
receipts_root: Bytes32
logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM]
prev_randao: Bytes32
block_number: uint64
gas_limit: uint64
gas_used: uint64
timestamp: uint64
extra_data: ByteList[MAX_EXTRA_DATA_BYTES]
base_fee_per_gas: uint256
# Extra payload fields
block_hash: Hash32
transactions: List[Transaction, MAX_TRANSACTIONS_PER_PAYLOAD]
withdrawals: List[Withdrawal, MAX_WITHDRAWALS_PER_PAYLOAD]
blob_gas_used: uint64
excess_blob_gas: uint64
deposit_requests: List[DepositRequest, MAX_DEPOSIT_REQUESTS_PER_PAYLOAD] # [New in Electra:EIP6110]
# [New in Electra:EIP7002:EIP7251]
withdrawal_requests: List[WithdrawalRequest, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD]
# [New in Electra:EIP7251]
consolidation_requests: List[ConsolidationRequest, MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD]
```
#### `ExecutionPayloadHeader`
```python
class ExecutionPayloadHeader(Container):
# Execution block header fields
parent_hash: Hash32
fee_recipient: ExecutionAddress
state_root: Bytes32
receipts_root: Bytes32
logs_bloom: ByteVector[BYTES_PER_LOGS_BLOOM]
prev_randao: Bytes32
block_number: uint64
gas_limit: uint64
gas_used: uint64
timestamp: uint64
extra_data: ByteList[MAX_EXTRA_DATA_BYTES]
base_fee_per_gas: uint256
# Extra payload fields
block_hash: Hash32
transactions_root: Root
withdrawals_root: Root
blob_gas_used: uint64
excess_blob_gas: uint64
deposit_requests_root: Root # [New in Electra:EIP6110]
withdrawal_requests_root: Root # [New in Electra:EIP7002:EIP7251]
consolidation_requests_root: Root # [New in Electra:EIP7251]
```
#### `BeaconState`
```python
@ -419,7 +372,7 @@ class BeaconState(Container):
current_sync_committee: SyncCommittee
next_sync_committee: SyncCommittee
# Execution
latest_execution_payload_header: ExecutionPayloadHeader # [Modified in Electra:EIP6110:EIP7002]
latest_execution_payload_header: ExecutionPayloadHeader
# Withdrawals
next_withdrawal_index: WithdrawalIndex
next_withdrawal_validator_index: ValidatorIndex
@ -524,14 +477,14 @@ def is_fully_withdrawable_validator(validator: Validator, balance: Gwei, epoch:
#### Modified `is_partially_withdrawable_validator`
*Note*: The function `is_partially_withdrawable_validator` is modified to use `get_validator_max_effective_balance` instead of `MAX_EFFECTIVE_BALANCE` and `has_execution_withdrawal_credential` instead of `has_eth1_withdrawal_credential`.
*Note*: The function `is_partially_withdrawable_validator` is modified to use `get_max_effective_balance` instead of `MAX_EFFECTIVE_BALANCE` and `has_execution_withdrawal_credential` instead of `has_eth1_withdrawal_credential`.
```python
def is_partially_withdrawable_validator(validator: Validator, balance: Gwei) -> bool:
"""
Check if ``validator`` is partially withdrawable.
"""
max_effective_balance = get_validator_max_effective_balance(validator)
max_effective_balance = get_max_effective_balance(validator)
has_max_effective_balance = validator.effective_balance == max_effective_balance # [Modified in Electra:EIP7251]
has_excess_balance = balance > max_effective_balance # [Modified in Electra:EIP7251]
return (
@ -550,10 +503,10 @@ def get_committee_indices(committee_bits: Bitvector) -> Sequence[CommitteeIndex]
return [CommitteeIndex(index) for index, bit in enumerate(committee_bits) if bit]
```
#### New `get_validator_max_effective_balance`
#### New `get_max_effective_balance`
```python
def get_validator_max_effective_balance(validator: Validator) -> Gwei:
def get_max_effective_balance(validator: Validator) -> Gwei:
"""
Get max effective balance for ``validator``.
"""
@ -600,7 +553,7 @@ def get_consolidation_churn_limit(state: BeaconState) -> Gwei:
```python
def get_active_balance(state: BeaconState, validator_index: ValidatorIndex) -> Gwei:
max_effective_balance = get_validator_max_effective_balance(state.validators[validator_index])
max_effective_balance = get_max_effective_balance(state.validators[validator_index])
return min(state.balances[validator_index], max_effective_balance)
```
@ -825,7 +778,7 @@ def process_epoch(state: BeaconState) -> None:
process_inactivity_updates(state)
process_rewards_and_penalties(state)
process_registry_updates(state) # [Modified in Electra:EIP7251]
process_slashings(state)
process_slashings(state) # [Modified in Electra:EIP7251]
process_eth1_data_reset(state)
process_pending_balance_deposits(state) # [New in Electra:EIP7251]
process_pending_consolidations(state) # [New in Electra:EIP7251]
@ -862,6 +815,28 @@ def process_registry_updates(state: BeaconState) -> None:
validator.activation_epoch = activation_epoch
```
#### Modified `process_slashings`
*Note*: The function `process_slashings` is modified to use a new algorithm to compute correlation penalty.
```python
def process_slashings(state: BeaconState) -> None:
epoch = get_current_epoch(state)
total_balance = get_total_active_balance(state)
adjusted_total_slashing_balance = min(
sum(state.slashings) * PROPORTIONAL_SLASHING_MULTIPLIER_BELLATRIX,
total_balance
)
increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from total balance to avoid uint64 overflow
penalty_per_effective_balance_increment = adjusted_total_slashing_balance // (total_balance // increment)
for index, validator in enumerate(state.validators):
if validator.slashed and epoch + EPOCHS_PER_SLASHINGS_VECTOR // 2 == validator.withdrawable_epoch:
effective_balance_increments = validator.effective_balance // increment
# [Modified in Electra:EIP7251]
penalty = penalty_per_effective_balance_increment * effective_balance_increments
decrease_balance(state, ValidatorIndex(index), penalty)
```
#### New `process_pending_balance_deposits`
```python
@ -953,6 +928,70 @@ def process_effective_balance_updates(state: BeaconState) -> None:
validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, EFFECTIVE_BALANCE_LIMIT)
```
### Execution engine
#### Request data
##### Modified `NewPayloadRequest`
```python
@dataclass
class NewPayloadRequest(object):
execution_payload: ExecutionPayload
versioned_hashes: Sequence[VersionedHash]
parent_beacon_block_root: Root
execution_requests: ExecutionRequests # [New in Electra]
```
#### Engine APIs
##### Modified `notify_new_payload`
*Note*: The function `notify_new_payload` is modified to include the additional `execution_requests` parameter in Electra.
```python
def notify_new_payload(self: ExecutionEngine,
execution_payload: ExecutionPayload,
execution_requests: ExecutionRequests,
parent_beacon_block_root: Root) -> bool:
"""
Return ``True`` if and only if ``execution_payload`` and ``execution_requests``
are valid with respect to ``self.execution_state``.
"""
...
```
##### Modified `verify_and_notify_new_payload`
*Note*: The function `verify_and_notify_new_payload` is modified to pass the additional parameter `execution_requests`
when calling `notify_new_payload` in Electra.
```python
def verify_and_notify_new_payload(self: ExecutionEngine,
new_payload_request: NewPayloadRequest) -> bool:
"""
Return ``True`` if and only if ``new_payload_request`` is valid with respect to ``self.execution_state``.
"""
execution_payload = new_payload_request.execution_payload
execution_requests = new_payload_request.execution_requests # [New in Electra]
parent_beacon_block_root = new_payload_request.parent_beacon_block_root
if not self.is_valid_block_hash(execution_payload, parent_beacon_block_root):
return False
if not self.is_valid_versioned_hashes(new_payload_request):
return False
# [Modified in Electra]
if not self.notify_new_payload(
execution_payload,
execution_requests,
parent_beacon_block_root):
return False
return True
```
### Block processing
```python
@ -1017,7 +1056,7 @@ def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal],
index=withdrawal_index,
validator_index=validator_index,
address=ExecutionAddress(validator.withdrawal_credentials[12:]),
amount=balance - get_validator_max_effective_balance(validator), # [Modified in Electra:EIP7251]
amount=balance - get_max_effective_balance(validator), # [Modified in Electra:EIP7251]
))
withdrawal_index += WithdrawalIndex(1)
if len(withdrawals) == MAX_WITHDRAWALS_PER_PAYLOAD:
@ -1034,10 +1073,9 @@ def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal],
def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
expected_withdrawals, partial_withdrawals_count = get_expected_withdrawals(state) # [Modified in Electra:EIP7251]
assert len(payload.withdrawals) == len(expected_withdrawals)
assert payload.withdrawals == expected_withdrawals
for expected_withdrawal, withdrawal in zip(expected_withdrawals, payload.withdrawals):
assert withdrawal == expected_withdrawal
for withdrawal in expected_withdrawals:
decrease_balance(state, withdrawal.validator_index, withdrawal.amount)
# Update pending partial withdrawals [New in Electra:EIP7251]
@ -1064,7 +1102,7 @@ def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
##### Modified `process_execution_payload`
*Note*: The function `process_execution_payload` is modified to use the new `ExecutionPayloadHeader` type.
*Note*: The function `process_execution_payload` is modified to pass `execution_requests` into `execution_engine.verify_and_notify_new_payload` (via the updated `NewPayloadRequest`).
```python
def process_execution_payload(state: BeaconState, body: BeaconBlockBody, execution_engine: ExecutionEngine) -> None:
@ -1083,6 +1121,7 @@ def process_execution_payload(state: BeaconState, body: BeaconBlockBody, executi
assert execution_engine.verify_and_notify_new_payload(
NewPayloadRequest(
execution_payload=payload,
execution_requests=body.execution_requests, # [New in Electra]
versioned_hashes=versioned_hashes,
parent_beacon_block_root=state.latest_block_header.parent_root,
)
@ -1106,9 +1145,6 @@ def process_execution_payload(state: BeaconState, body: BeaconBlockBody, executi
withdrawals_root=hash_tree_root(payload.withdrawals),
blob_gas_used=payload.blob_gas_used,
excess_blob_gas=payload.excess_blob_gas,
deposit_requests_root=hash_tree_root(payload.deposit_requests), # [New in Electra:EIP6110]
withdrawal_requests_root=hash_tree_root(payload.withdrawal_requests), # [New in Electra:EIP7002:EIP7251]
consolidation_requests_root=hash_tree_root(payload.consolidation_requests), # [New in Electra:EIP7251]
)
```
@ -1135,14 +1171,12 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
for_ops(body.proposer_slashings, process_proposer_slashing)
for_ops(body.attester_slashings, process_attester_slashing)
for_ops(body.attestations, process_attestation) # [Modified in Electra:EIP7549]
for_ops(body.deposits, process_deposit) # [Modified in Electra:EIP7251]
for_ops(body.deposits, process_deposit)
for_ops(body.voluntary_exits, process_voluntary_exit) # [Modified in Electra:EIP7251]
for_ops(body.bls_to_execution_changes, process_bls_to_execution_change)
for_ops(body.execution_payload.deposit_requests, process_deposit_request) # [New in Electra:EIP6110]
# [New in Electra:EIP7002:EIP7251]
for_ops(body.execution_payload.withdrawal_requests, process_withdrawal_request)
# [New in Electra:EIP7251]
for_ops(body.execution_payload.consolidation_requests, process_consolidation_request)
for_ops(body.execution_requests.deposits, process_deposit_request) # [New in Electra:EIP6110]
for_ops(body.execution_requests.withdrawals, process_withdrawal_request) # [New in Electra:EIP7002:EIP7251]
for_ops(body.execution_requests.consolidations, process_consolidation_request) # [New in Electra:EIP7251]
```
##### Attestations
@ -1196,7 +1230,7 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
###### Modified `apply_deposit`
*Note*: The function `process_deposit` is modified to support EIP7251.
*Note*: The function `apply_deposit` is modified to support EIP7251.
```python
def apply_deposit(state: BeaconState,
@ -1508,7 +1542,7 @@ def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32,
balance = state.balances[index]
# [Modified in Electra:EIP7251]
validator.effective_balance = min(
balance - balance % EFFECTIVE_BALANCE_INCREMENT, get_validator_max_effective_balance(validator))
balance - balance % EFFECTIVE_BALANCE_INCREMENT, get_max_effective_balance(validator))
if validator.effective_balance >= MIN_ACTIVATION_BALANCE:
validator.activation_eligibility_epoch = GENESIS_EPOCH
validator.activation_epoch = GENESIS_EPOCH

View File

@ -72,28 +72,7 @@ an irregular state change is made to upgrade to Electra.
```python
def upgrade_to_electra(pre: deneb.BeaconState) -> BeaconState:
epoch = deneb.get_current_epoch(pre)
latest_execution_payload_header = ExecutionPayloadHeader(
parent_hash=pre.latest_execution_payload_header.parent_hash,
fee_recipient=pre.latest_execution_payload_header.fee_recipient,
state_root=pre.latest_execution_payload_header.state_root,
receipts_root=pre.latest_execution_payload_header.receipts_root,
logs_bloom=pre.latest_execution_payload_header.logs_bloom,
prev_randao=pre.latest_execution_payload_header.prev_randao,
block_number=pre.latest_execution_payload_header.block_number,
gas_limit=pre.latest_execution_payload_header.gas_limit,
gas_used=pre.latest_execution_payload_header.gas_used,
timestamp=pre.latest_execution_payload_header.timestamp,
extra_data=pre.latest_execution_payload_header.extra_data,
base_fee_per_gas=pre.latest_execution_payload_header.base_fee_per_gas,
block_hash=pre.latest_execution_payload_header.block_hash,
transactions_root=pre.latest_execution_payload_header.transactions_root,
withdrawals_root=pre.latest_execution_payload_header.withdrawals_root,
blob_gas_used=pre.latest_execution_payload_header.blob_gas_used,
excess_blob_gas=pre.latest_execution_payload_header.excess_blob_gas,
deposit_requests_root=Root(), # [New in Electra:EIP6110]
withdrawal_requests_root=Root(), # [New in Electra:EIP7002]
consolidation_requests_root=Root(), # [New in Electra:EIP7251]
)
latest_execution_payload_header = pre.latest_execution_payload_header
exit_epochs = [v.exit_epoch for v in pre.validators if v.exit_epoch != FAR_FUTURE_EPOCH]
if not exit_epochs:

View File

@ -39,28 +39,7 @@ A Electra `LightClientStore` can still process earlier light client data. In ord
def upgrade_lc_header_to_electra(pre: deneb.LightClientHeader) -> LightClientHeader:
return LightClientHeader(
beacon=pre.beacon,
execution=ExecutionPayloadHeader(
parent_hash=pre.execution.parent_hash,
fee_recipient=pre.execution.fee_recipient,
state_root=pre.execution.state_root,
receipts_root=pre.execution.receipts_root,
logs_bloom=pre.execution.logs_bloom,
prev_randao=pre.execution.prev_randao,
block_number=pre.execution.block_number,
gas_limit=pre.execution.gas_limit,
gas_used=pre.execution.gas_used,
timestamp=pre.execution.timestamp,
extra_data=pre.execution.extra_data,
base_fee_per_gas=pre.execution.base_fee_per_gas,
block_hash=pre.execution.block_hash,
transactions_root=pre.execution.transactions_root,
withdrawals_root=pre.execution.withdrawals_root,
blob_gas_used=pre.execution.blob_gas_used,
excess_blob_gas=pre.execution.blob_gas_used,
deposit_requests_root=Root(), # [New in Electra:EIP6110]
withdrawal_requests_root=Root(), # [New in Electra:EIP7002:EIP7251]
consolidation_requests_root=Root(), # [New in Electra:EIP7251]
),
execution=pre.execution,
execution_branch=pre.execution_branch,
)
```

View File

@ -50,12 +50,6 @@ def block_to_light_client_header(block: SignedBeaconBlock) -> LightClientHeader:
execution_header.blob_gas_used = payload.blob_gas_used
execution_header.excess_blob_gas = payload.excess_blob_gas
# [New in Electra:EIP6110:EIP7002:EIP7251]
if epoch >= ELECTRA_FORK_EPOCH:
execution_header.deposit_requests_root = hash_tree_root(payload.deposit_requests)
execution_header.withdrawal_requests_root = hash_tree_root(payload.withdrawal_requests)
execution_header.consolidation_requests_root = hash_tree_root(payload.consolidation_requests)
execution_branch = ExecutionBranch(
compute_merkle_proof(block.message.body, EXECUTION_PAYLOAD_GINDEX))
else:

View File

@ -159,15 +159,6 @@ def get_lc_execution_root(header: LightClientHeader) -> Root:
def is_valid_light_client_header(header: LightClientHeader) -> bool:
epoch = compute_epoch_at_slot(header.beacon.slot)
# [New in Electra:EIP6110:EIP7002:EIP7251]
if epoch < ELECTRA_FORK_EPOCH:
if (
header.execution.deposit_requests_root != Root()
or header.execution.withdrawal_requests_root != Root()
or header.execution.consolidation_requests_root != Root()
):
return False
if epoch < DENEB_FORK_EPOCH:
if header.execution.blob_gas_used != uint64(0) or header.execution.excess_blob_gas != uint64(0):
return False

View File

@ -257,7 +257,7 @@ We define a Merkle multiproof as a minimal subset of nodes in a Merkle tree need
x x . . . . x *
```
. are unused nodes, * are used nodes, x are the values we are trying to prove. Notice how despite being a multiproof for 3 values, it requires only 3 auxiliary nodes, only one node more than would be required to prove a single value. Normally the efficiency gains are not quite that extreme, but the savings relative to individual Merkle proofs are still significant. As a rule of thumb, a multiproof for k nodes at the same level of an n-node tree has size `k * (n/k + log(n/k))`.
. are unused nodes, * are used nodes, x are the values we are trying to prove. Notice how despite being a multiproof for 3 values, it requires only 3 auxiliary nodes, the same amount required to prove a single value. Normally the efficiency gains are not quite that extreme, but the savings relative to individual Merkle proofs are still significant. As a rule of thumb, a multiproof for k nodes at the same level of an n-node tree has size `k * (n/k + log(n/k))`.
First, we provide a method for computing the generalized indices of the auxiliary tree nodes that a proof of a given set of generalized indices will require:

View File

@ -1 +1 @@
1.5.0-alpha.5
1.5.0-alpha.6

View File

@ -16,7 +16,7 @@ def _run_get_custody_columns(spec, rng, node_id=None, custody_subnet_count=None)
result = spec.get_custody_columns(node_id, custody_subnet_count)
yield 'node_id', 'meta', node_id
yield 'custody_subnet_count', 'meta', custody_subnet_count
yield 'custody_subnet_count', 'meta', int(custody_subnet_count)
assert len(result) == len(set(result))
assert len(result) == (

View File

@ -1,3 +1,4 @@
import random
from eth2spec.test.context import (
spec_state_test,
expect_assertion_error,
@ -19,6 +20,31 @@ from eth2spec.test.helpers.withdrawals import (
@with_electra_and_later
@spec_state_test
def test_basic_withdrawal_request(spec, state):
rng = random.Random(1337)
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
spec, state, validator_index, address=address
)
withdrawal_request = spec.WithdrawalRequest(
source_address=address,
validator_pubkey=validator_pubkey,
amount=spec.FULL_EXIT_REQUEST_AMOUNT,
)
yield from run_withdrawal_request_processing(
spec, state, withdrawal_request
)
@with_electra_and_later
@spec_state_test
def test_basic_withdrawal_request_with_first_validator(spec, state):
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
@ -43,11 +69,12 @@ def test_basic_withdrawal_request(spec, state):
@with_electra_and_later
@spec_state_test
def test_basic_withdrawal_request_with_compounding_credentials(spec, state):
rng = random.Random(1338)
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
set_compounding_withdrawal_credential(spec, state, validator_index, address=address)
@ -66,9 +93,10 @@ def test_basic_withdrawal_request_with_compounding_credentials(spec, state):
@spec_state_test
@with_presets([MINIMAL], "need full partial withdrawal queue")
def test_basic_withdrawal_request_with_full_partial_withdrawal_queue(spec, state):
rng = random.Random(1339)
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
@ -102,11 +130,12 @@ def test_basic_withdrawal_request_with_full_partial_withdrawal_queue(spec, state
@with_electra_and_later
@spec_state_test
def test_incorrect_source_address(spec, state):
rng = random.Random(1340)
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
incorrect_address = b"\x33" * 20
@ -127,11 +156,12 @@ def test_incorrect_source_address(spec, state):
@with_electra_and_later
@spec_state_test
def test_incorrect_withdrawal_credential_prefix(spec, state):
rng = random.Random(1341)
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
@ -156,11 +186,12 @@ def test_incorrect_withdrawal_credential_prefix(spec, state):
@with_electra_and_later
@spec_state_test
def test_on_withdrawal_request_initiated_validator(spec, state):
rng = random.Random(1342)
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
@ -182,8 +213,9 @@ def test_on_withdrawal_request_initiated_validator(spec, state):
@with_electra_and_later
@spec_state_test
def test_activation_epoch_less_than_shard_committee_period(spec, state):
rng = random.Random(1343)
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
set_eth1_withdrawal_credential_with_balance(
@ -211,9 +243,10 @@ def test_activation_epoch_less_than_shard_committee_period(spec, state):
@spec_state_test
@with_presets([MINIMAL])
def test_basic_partial_withdrawal_request(spec, state):
rng = random.Random(1344)
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
@ -243,9 +276,10 @@ def test_basic_partial_withdrawal_request(spec, state):
@spec_state_test
@with_presets([MINIMAL])
def test_basic_partial_withdrawal_request_higher_excess_balance(spec, state):
rng = random.Random(1345)
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
@ -275,9 +309,10 @@ def test_basic_partial_withdrawal_request_higher_excess_balance(spec, state):
@spec_state_test
@with_presets([MINIMAL])
def test_basic_partial_withdrawal_request_lower_than_excess_balance(spec, state):
rng = random.Random(1346)
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
excess_balance = spec.EFFECTIVE_BALANCE_INCREMENT
@ -308,9 +343,10 @@ def test_basic_partial_withdrawal_request_lower_than_excess_balance(spec, state)
@spec_state_test
@with_presets([MINIMAL])
def test_partial_withdrawal_request_with_pending_withdrawals(spec, state):
rng = random.Random(1347)
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
@ -349,9 +385,10 @@ def test_partial_withdrawal_request_with_pending_withdrawals(spec, state):
def test_partial_withdrawal_request_with_pending_withdrawals_and_high_amount(
spec, state
):
rng = random.Random(1348)
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.UINT64_MAX
@ -387,9 +424,10 @@ def test_partial_withdrawal_request_with_pending_withdrawals_and_high_amount(
@spec_state_test
@with_presets([MINIMAL])
def test_partial_withdrawal_request_with_high_balance(spec, state):
rng = random.Random(1349)
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.MAX_EFFECTIVE_BALANCE_ELECTRA
@ -424,9 +462,10 @@ def test_partial_withdrawal_request_with_high_balance(spec, state):
@spec_state_test
@with_presets([MINIMAL])
def test_partial_withdrawal_request_with_high_amount(spec, state):
rng = random.Random(1350)
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
# Set high amount requested to withdraw
@ -457,9 +496,10 @@ def test_partial_withdrawal_request_with_high_amount(spec, state):
@spec_state_test
@with_presets([MINIMAL])
def test_partial_withdrawal_request_with_low_amount(spec, state):
rng = random.Random(1351)
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = 1
@ -492,9 +532,10 @@ def test_partial_withdrawal_request_with_low_amount(spec, state):
@spec_state_test
@with_presets([MINIMAL], "need full partial withdrawal queue")
def test_partial_withdrawal_queue_full(spec, state):
rng = random.Random(1352)
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
@ -522,9 +563,10 @@ def test_partial_withdrawal_queue_full(spec, state):
@with_electra_and_later
@spec_state_test
def test_no_compounding_credentials(spec, state):
rng = random.Random(1353)
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
@ -551,9 +593,10 @@ def test_no_compounding_credentials(spec, state):
@with_electra_and_later
@spec_state_test
def test_no_excess_balance(spec, state):
rng = random.Random(1354)
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
@ -573,9 +616,10 @@ def test_no_excess_balance(spec, state):
@with_electra_and_later
@spec_state_test
def test_pending_withdrawals_consume_all_excess_balance(spec, state):
rng = random.Random(1355)
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
@ -603,9 +647,10 @@ def test_pending_withdrawals_consume_all_excess_balance(spec, state):
@with_electra_and_later
@spec_state_test
def test_insufficient_effective_balance(spec, state):
rng = random.Random(1356)
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
@ -632,11 +677,12 @@ def test_insufficient_effective_balance(spec, state):
@with_electra_and_later
@spec_state_test
def test_partial_withdrawal_incorrect_source_address(spec, state):
rng = random.Random(1357)
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
incorrect_address = b"\x33" * 20
@ -658,11 +704,12 @@ def test_partial_withdrawal_incorrect_source_address(spec, state):
@with_electra_and_later
@spec_state_test
def test_partial_withdrawal_incorrect_withdrawal_credential_prefix(spec, state):
rng = random.Random(1358)
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
@ -687,11 +734,12 @@ def test_partial_withdrawal_incorrect_withdrawal_credential_prefix(spec, state):
@with_electra_and_later
@spec_state_test
def test_partial_withdrawal_on_exit_initiated_validator(spec, state):
rng = random.Random(1359)
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT
@ -715,8 +763,9 @@ def test_partial_withdrawal_on_exit_initiated_validator(spec, state):
def test_partial_withdrawal_activation_epoch_less_than_shard_committee_period(
spec, state
):
rng = random.Random(1360)
current_epoch = spec.get_current_epoch(state)
validator_index = spec.get_active_validator_indices(state, current_epoch)[0]
validator_index = rng.choice(spec.get_active_validator_indices(state, current_epoch))
validator_pubkey = state.validators[validator_index].pubkey
address = b"\x22" * 20
amount = spec.EFFECTIVE_BALANCE_INCREMENT

View File

@ -1,6 +1,6 @@
from eth2spec.test.helpers.epoch_processing import (
run_epoch_processing_with,
compute_state_by_epoch_processing_to,
run_epoch_processing_to,
)
from eth2spec.test.context import (
spec_state_test,
@ -219,7 +219,8 @@ def test_pending_consolidation_future_epoch(spec, state):
next_epoch_with_full_participation(spec, state)
# Obtain state before the call to process_pending_consolidations
state_before_consolidation = compute_state_by_epoch_processing_to(spec, state, "process_pending_consolidations")
state_before_consolidation = state.copy()
run_epoch_processing_to(spec, state_before_consolidation, "process_pending_consolidations")
yield from run_epoch_processing_with(spec, state, "process_pending_consolidations")
@ -270,7 +271,8 @@ def test_pending_consolidation_compounding_creds(spec, state):
next_epoch_with_full_participation(spec, state)
# Obtain state before the call to process_pending_consolidations
state_before_consolidation = compute_state_by_epoch_processing_to(spec, state, "process_pending_consolidations")
state_before_consolidation = state.copy()
run_epoch_processing_to(spec, state_before_consolidation, "process_pending_consolidations")
yield from run_epoch_processing_with(spec, state, "process_pending_consolidations")
@ -325,7 +327,8 @@ def test_pending_consolidation_with_pending_deposit(spec, state):
next_epoch_with_full_participation(spec, state)
# Obtain state before the call to process_pending_balance_deposits
state_before_consolidation = compute_state_by_epoch_processing_to(spec, state, "process_pending_balance_deposits")
state_before_consolidation = state.copy()
run_epoch_processing_to(spec, state_before_consolidation, "process_pending_consolidations")
yield from run_epoch_processing_with(spec, state, "process_pending_consolidations")

View File

@ -41,7 +41,7 @@ def test_basic_el_withdrawal_request(spec, state):
validator_pubkey=validator_pubkey,
)
block = build_empty_block_for_next_slot(spec, state)
block.body.execution_payload.withdrawal_requests = [withdrawal_request]
block.body.execution_requests.withdrawals = [withdrawal_request]
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state)
signed_block = state_transition_and_sign_block(spec, state, block)
@ -77,7 +77,7 @@ def test_basic_btec_and_el_withdrawal_request_in_same_block(spec, state):
source_address=address,
validator_pubkey=validator_pubkey,
)
block.body.execution_payload.withdrawal_requests = [withdrawal_request]
block.body.execution_requests.withdrawals = [withdrawal_request]
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state)
signed_block = state_transition_and_sign_block(spec, state, block)
@ -130,7 +130,7 @@ def test_basic_btec_before_el_withdrawal_request(spec, state):
validator_pubkey=validator_pubkey,
)
block_2 = build_empty_block_for_next_slot(spec, state)
block_2.body.execution_payload.withdrawal_requests = [withdrawal_request]
block_2.body.execution_requests.withdrawals = [withdrawal_request]
block_2.body.execution_payload.block_hash = compute_el_block_hash(spec, block_2.body.execution_payload, state)
signed_block_2 = state_transition_and_sign_block(spec, state, block_2)
@ -163,7 +163,7 @@ def test_cl_exit_and_el_withdrawal_request_in_same_block(spec, state):
)
block = build_empty_block_for_next_slot(spec, state)
block.body.voluntary_exits = signed_voluntary_exits
block.body.execution_payload.withdrawal_requests = [withdrawal_request]
block.body.execution_requests.withdrawals = [withdrawal_request]
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state)
signed_block = state_transition_and_sign_block(spec, state, block)

View File

@ -38,7 +38,7 @@ def run_deposit_transition_block(spec, state, block, top_up_keys=[], valid=True)
# Check that deposits are applied
if valid:
expected_pubkeys = [d.data.pubkey for d in block.body.deposits]
deposit_requests = block.body.execution_payload.deposit_requests
deposit_requests = block.body.execution_requests.deposits
expected_pubkeys = expected_pubkeys + [d.pubkey for d in deposit_requests if (d.pubkey not in top_up_keys)]
actual_pubkeys = [v.pubkey for v in state.validators[len(state.validators) - len(expected_pubkeys):]]
@ -102,7 +102,7 @@ def prepare_state_and_block(spec,
# Assign deposits and deposit requests
block.body.deposits = deposits
block.body.execution_payload.deposit_requests = deposit_requests
block.body.execution_requests.deposits = deposit_requests
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state)
return state, block
@ -120,7 +120,7 @@ def test_deposit_transition__start_index_is_set(spec, state):
yield from run_deposit_transition_block(spec, state, block)
# deposit_requests_start_index must be set to the index of the first request
assert state.deposit_requests_start_index == block.body.execution_payload.deposit_requests[0].index
assert state.deposit_requests_start_index == block.body.execution_requests.deposits[0].index
@with_phases([ELECTRA])
@ -219,7 +219,7 @@ def test_deposit_transition__deposit_and_top_up_same_block(spec, state):
# Artificially assign deposit's pubkey to a deposit request of the same block
top_up_keys = [block.body.deposits[0].data.pubkey]
block.body.execution_payload.deposit_requests[0].pubkey = top_up_keys[0]
block.body.execution_requests.deposits[0].pubkey = top_up_keys[0]
block.body.execution_payload.block_hash = compute_el_block_hash(spec, block.body.execution_payload, state)
pre_pending_deposits = len(state.pending_balance_deposits)
@ -229,5 +229,5 @@ def test_deposit_transition__deposit_and_top_up_same_block(spec, state):
# Check the top up
assert len(state.pending_balance_deposits) == pre_pending_deposits + 2
assert state.pending_balance_deposits[pre_pending_deposits].amount == block.body.deposits[0].data.amount
amount_from_deposit = block.body.execution_payload.deposit_requests[0].amount
amount_from_deposit = block.body.execution_requests.deposits[0].amount
assert state.pending_balance_deposits[pre_pending_deposits + 1].amount == amount_from_deposit

View File

@ -1,6 +1,7 @@
from eth2spec.test.helpers.execution_payload import build_empty_execution_payload
from eth2spec.test.helpers.execution_payload import build_empty_signed_execution_payload_header
from eth2spec.test.helpers.forks import is_post_whisk, is_post_altair, is_post_bellatrix, is_post_eip7732
from eth2spec.test.helpers.forks import is_post_whisk, is_post_altair, is_post_bellatrix, is_post_eip7732, \
is_post_electra
from eth2spec.test.helpers.keys import privkeys, whisk_ks_initial, whisk_ks_final
from eth2spec.utils import bls
from eth2spec.utils.bls import only_with_bls
@ -126,6 +127,11 @@ def build_empty_block(spec, state, slot=None, proposer_index=None):
if is_post_bellatrix(spec):
empty_block.body.execution_payload = build_empty_execution_payload(spec, state)
if is_post_electra(spec):
empty_block.body.execution_requests.deposits = []
empty_block.body.execution_requests.withdrawals = []
empty_block.body.execution_requests.consolidations = []
if is_post_whisk(spec):
# Whisk opening proof
#######

View File

@ -38,13 +38,14 @@ def run_fork_test(post_spec, pre_state):
'next_withdrawal_index', 'next_withdrawal_validator_index',
# Deep history valid from Capella onwards
'historical_summaries',
'latest_execution_payload_header'
]
for field in stable_fields:
assert getattr(pre_state, field) == getattr(post_state, field)
# Modified fields
modified_fields = ['fork', 'latest_execution_payload_header']
modified_fields = ['fork']
for field in modified_fields:
assert getattr(pre_state, field) != getattr(post_state, field)

View File

@ -36,8 +36,6 @@ def get_process_calls(spec):
'process_participation_record_updates'
),
'process_sync_committee_updates', # altair
'process_full_withdrawals', # capella
'process_partial_withdrawals', # capella
# TODO: add sharding processing functions when spec stabilizes.
]
@ -74,9 +72,3 @@ def run_epoch_processing_with(spec, state, process_name: str):
yield 'pre', state
getattr(spec, process_name)(state)
yield 'post', state
def compute_state_by_epoch_processing_to(spec, state, process_name: str):
state_copy = state.copy()
run_epoch_processing_to(spec, state_copy, process_name)
return state_copy

View File

@ -7,12 +7,7 @@ from eth2spec.test.helpers.keys import privkeys
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
from eth2spec.debug.random_value import get_random_bytes_list
from eth2spec.test.helpers.withdrawals import get_expected_withdrawals
from eth2spec.test.helpers.forks import (
is_post_capella,
is_post_deneb,
is_post_electra,
is_post_eip7732,
)
from eth2spec.test.helpers.forks import is_post_capella, is_post_deneb, is_post_eip7732
def get_execution_payload_header(spec, execution_payload):
@ -49,10 +44,6 @@ def get_execution_payload_header(spec, execution_payload):
if is_post_deneb(spec):
payload_header.blob_gas_used = execution_payload.blob_gas_used
payload_header.excess_blob_gas = execution_payload.excess_blob_gas
if is_post_electra(spec):
payload_header.deposit_requests_root = spec.hash_tree_root(execution_payload.deposit_requests)
payload_header.withdrawal_requests_root = spec.hash_tree_root(execution_payload.withdrawal_requests)
payload_header.consolidation_requests_root = spec.hash_tree_root(execution_payload.consolidation_requests)
return payload_header
@ -74,8 +65,7 @@ def compute_el_header_block_hash(spec,
payload_header,
transactions_trie_root,
withdrawals_trie_root=None,
parent_beacon_block_root=None,
requests_trie_root=None):
parent_beacon_block_root=None):
"""
Computes the RLP execution block hash described by an `ExecutionPayloadHeader`.
"""
@ -126,9 +116,6 @@ def compute_el_header_block_hash(spec,
execution_payload_header_rlp.append((big_endian_int, payload_header.excess_blob_gas))
# parent_beacon_root
execution_payload_header_rlp.append((Binary(32, 32), parent_beacon_block_root))
if is_post_electra(spec):
# requests_root
execution_payload_header_rlp.append((Binary(32, 32), requests_trie_root))
sedes = List([schema for schema, _ in execution_payload_header_rlp])
values = [value for _, value in execution_payload_header_rlp]
@ -204,25 +191,19 @@ def get_consolidation_request_rlp_bytes(consolidation_request):
return b"\x02" + encode(values, sedes)
def compute_el_block_hash(spec, payload, pre_state):
def compute_el_block_hash_with_parent_root(spec, payload, parent_beacon_block_root):
if payload == spec.ExecutionPayload():
return spec.Hash32()
transactions_trie_root = compute_trie_root_from_indexed_data(payload.transactions)
withdrawals_trie_root = None
parent_beacon_block_root = None
requests_trie_root = None
if is_post_capella(spec):
withdrawals_encoded = [get_withdrawal_rlp(withdrawal) for withdrawal in payload.withdrawals]
withdrawals_trie_root = compute_trie_root_from_indexed_data(withdrawals_encoded)
if is_post_deneb(spec):
parent_beacon_block_root = pre_state.latest_block_header.hash_tree_root()
if is_post_electra(spec):
requests_encoded = []
requests_encoded += [get_deposit_request_rlp_bytes(request) for request in payload.deposit_requests]
requests_encoded += [get_withdrawal_request_rlp_bytes(request) for request in payload.withdrawal_requests]
requests_encoded += [get_consolidation_request_rlp_bytes(request) for request in payload.consolidation_requests]
requests_trie_root = compute_trie_root_from_indexed_data(requests_encoded)
if not is_post_deneb(spec):
parent_beacon_block_root = None
payload_header = get_execution_payload_header(spec, payload)
@ -232,10 +213,27 @@ def compute_el_block_hash(spec, payload, pre_state):
transactions_trie_root,
withdrawals_trie_root,
parent_beacon_block_root,
requests_trie_root,
)
def compute_el_block_hash(spec, payload, pre_state):
parent_beacon_block_root = None
if is_post_deneb(spec):
previous_block_header = pre_state.latest_block_header.copy()
if previous_block_header.state_root == spec.Root():
previous_block_header.state_root = pre_state.hash_tree_root()
parent_beacon_block_root = previous_block_header.hash_tree_root()
return compute_el_block_hash_with_parent_root(
spec, payload, parent_beacon_block_root)
def compute_el_block_hash_for_block(spec, block):
return compute_el_block_hash_with_parent_root(
spec, block.body.execution_payload, block.parent_root)
def build_empty_post_eip7732_execution_payload_header(spec, state):
if not is_post_eip7732(spec):
return
@ -296,10 +294,6 @@ def build_empty_execution_payload(spec, state, randao_mix=None):
if is_post_deneb(spec):
payload.blob_gas_used = 0
payload.excess_blob_gas = 0
if is_post_electra(spec):
payload.deposit_requests = []
payload.withdrawal_requests = []
payload.consolidation_requests = []
payload.block_hash = compute_el_block_hash(spec, payload, state)

View File

@ -66,14 +66,11 @@ def get_sample_genesis_execution_payload_header(spec,
transactions_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
withdrawals_trie_root = None
parent_beacon_block_root = None
requests_trie_root = None
if is_post_capella(spec):
withdrawals_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
if is_post_deneb(spec):
parent_beacon_block_root = bytes.fromhex("0000000000000000000000000000000000000000000000000000000000000000")
if is_post_electra(spec):
requests_trie_root = bytes.fromhex("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
payload_header.block_hash = compute_el_header_block_hash(
spec,
@ -81,7 +78,6 @@ def get_sample_genesis_execution_payload_header(spec,
transactions_trie_root,
withdrawals_trie_root,
parent_beacon_block_root,
requests_trie_root,
)
return payload_header

View File

@ -2,6 +2,8 @@ from copy import deepcopy
from eth2spec.test.context import spec_state_test, expect_assertion_error, with_all_phases
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
from eth2spec.test.helpers.execution_payload import compute_el_block_hash_for_block
from eth2spec.test.helpers.forks import is_post_bellatrix
from eth2spec.test.helpers.state import next_slot
@ -65,6 +67,8 @@ def test_invalid_proposer_index(spec, state):
def test_invalid_parent_root(spec, state):
block = build_empty_block_for_next_slot(spec, state)
block.parent_root = b'\12' * 32 # invalid prev root
if is_post_bellatrix(spec):
block.body.execution_payload.block_hash = compute_el_block_hash_for_block(spec, block)
yield from run_block_header_processing(spec, state, block, valid=False)
@ -81,6 +85,8 @@ def test_invalid_multiple_blocks_single_slot(spec, state):
child_block = block.copy()
child_block.parent_root = block.hash_tree_root()
if is_post_bellatrix(spec):
child_block.body.execution_payload.block_hash = compute_el_block_hash_for_block(spec, child_block)
yield from run_block_header_processing(spec, state, child_block, prepare_state=False, valid=False)

View File

@ -3,7 +3,11 @@ from eth2spec.test.context import spec_state_test, with_all_phases
from eth2spec.test.helpers.epoch_processing import (
run_epoch_processing_with, run_epoch_processing_to
)
from eth2spec.test.helpers.forks import is_post_altair, is_post_bellatrix
from eth2spec.test.helpers.forks import (
is_post_altair,
is_post_bellatrix,
is_post_electra,
)
from eth2spec.test.helpers.random import randomize_state
from eth2spec.test.helpers.state import has_active_balance_differential
from eth2spec.test.helpers.voluntary_exits import get_unslashed_exited_validators
@ -40,6 +44,18 @@ def get_slashing_multiplier(spec):
return spec.PROPORTIONAL_SLASHING_MULTIPLIER
def _compute_expected_correlation_penalty(spec, effective_balance, total_slashed_balance, total_balance):
if is_post_electra(spec):
return ((get_slashing_multiplier(spec) * total_slashed_balance)
// (total_balance // spec.EFFECTIVE_BALANCE_INCREMENT)
* (effective_balance // spec.EFFECTIVE_BALANCE_INCREMENT))
else:
return (effective_balance // spec.EFFECTIVE_BALANCE_INCREMENT
* (get_slashing_multiplier(spec) * total_slashed_balance)
// total_balance
* spec.EFFECTIVE_BALANCE_INCREMENT)
def _setup_process_slashings_test(spec, state, not_slashable_set=set()):
# Slashed count to ensure that enough validators are slashed to induce maximum penalties
slashed_count = min(
@ -99,7 +115,8 @@ def test_minimal_penalty(spec, state):
#
# Just the bare minimum for this one validator
state.balances[0] = state.validators[0].effective_balance = spec.config.EJECTION_BALANCE
state.balances[0] = state.validators[0].effective_balance = (
spec.config.EJECTION_BALANCE + spec.EFFECTIVE_BALANCE_INCREMENT)
# All the other validators get the maximum.
for i in range(1, len(state.validators)):
state.validators[i].effective_balance = state.balances[i] = spec.MAX_EFFECTIVE_BALANCE
@ -119,15 +136,10 @@ def test_minimal_penalty(spec, state):
spec.process_slashings(state)
yield 'post', state
expected_penalty = (
state.validators[0].effective_balance // spec.EFFECTIVE_BALANCE_INCREMENT
* (get_slashing_multiplier(spec) * total_penalties)
// total_balance
* spec.EFFECTIVE_BALANCE_INCREMENT
)
expected_penalty = _compute_expected_correlation_penalty(
spec, state.validators[0].effective_balance, total_penalties, total_balance)
assert expected_penalty == 0
assert state.balances[0] == pre_slash_balances[0]
assert state.balances[0] == pre_slash_balances[0] - expected_penalty
@with_all_phases
@ -181,12 +193,8 @@ def test_scaled_penalties(spec, state):
for i in slashed_indices:
v = state.validators[i]
expected_penalty = (
v.effective_balance // spec.EFFECTIVE_BALANCE_INCREMENT
* (get_slashing_multiplier(spec) * total_penalties)
// (total_balance)
* spec.EFFECTIVE_BALANCE_INCREMENT
)
expected_penalty = _compute_expected_correlation_penalty(
spec, v.effective_balance, total_penalties, total_balance)
assert state.balances[i] == pre_slash_balances[i] - expected_penalty

View File

@ -18,6 +18,9 @@ from eth2spec.test.helpers.block import (
transition_unsigned_block,
sign_block,
)
from eth2spec.test.helpers.execution_payload import (
compute_el_block_hash_for_block,
)
from eth2spec.test.helpers.fork_choice import (
get_genesis_forkchoice_store_and_block,
on_tick_and_append_step,
@ -28,6 +31,9 @@ from eth2spec.test.helpers.fork_choice import (
is_ready_to_justify,
find_next_justifying_slot,
)
from eth2spec.test.helpers.forks import (
is_post_bellatrix,
)
from eth2spec.test.helpers.state import (
next_epoch,
next_slots,
@ -152,6 +158,8 @@ def test_on_block_bad_parent_root(spec, state):
block.state_root = state.hash_tree_root()
block.parent_root = b'\x45' * 32
if is_post_bellatrix(spec):
block.body.execution_payload.block_hash = compute_el_block_hash_for_block(spec, block)
signed_block = sign_block(spec, state, block)

View File

@ -20,7 +20,10 @@ from eth2spec.test.helpers.attester_slashings import (
from eth2spec.test.helpers.proposer_slashings import get_valid_proposer_slashing, check_proposer_slashing_effect
from eth2spec.test.helpers.attestations import get_valid_attestation
from eth2spec.test.helpers.deposits import prepare_state_and_deposit
from eth2spec.test.helpers.execution_payload import build_empty_execution_payload
from eth2spec.test.helpers.execution_payload import (
build_empty_execution_payload,
compute_el_block_hash_for_block,
)
from eth2spec.test.helpers.voluntary_exits import prepare_signed_exits
from eth2spec.test.helpers.multi_operations import (
run_slash_and_exit,
@ -158,7 +161,7 @@ def process_and_sign_block_without_header_validations(spec, state, block):
if is_post_altair(spec):
spec.process_sync_aggregate(state, block.body.sync_aggregate)
# Insert post-state rot
# Insert post-state root
block.state_root = state.hash_tree_root()
# Sign block
@ -197,11 +200,13 @@ def test_invalid_parent_from_same_slot(spec, state):
signed_parent_block = state_transition_and_sign_block(spec, state, parent_block)
child_block = parent_block.copy()
child_block.parent_root = state.latest_block_header.hash_tree_root()
if is_post_bellatrix(spec):
child_block.body.execution_payload = build_empty_execution_payload(spec, state)
child_block.parent_root = state.latest_block_header.hash_tree_root()
if is_post_bellatrix(spec):
child_block.body.execution_payload.block_hash = compute_el_block_hash_for_block(spec, child_block)
# Show that normal path through transition fails
failed_state = state.copy()
expect_assertion_error(

View File

@ -1,2 +0,0 @@
pytest>=4.4
../../../[generator]

View File

@ -1,2 +0,0 @@
pytest>=4.4
../../../[generator]

View File

@ -1,2 +0,0 @@
pytest>=4.4
../../../[generator]

View File

@ -1,2 +0,0 @@
pytest>=4.4
../../../[generator]

View File

@ -1,2 +0,0 @@
pytest>=4.4
../../../[generator]

View File

@ -1,2 +0,0 @@
pytest>=4.4
../../../[generator]

View File

@ -1,2 +0,0 @@
pytest>=4.4
../../../[generator]

View File

@ -1,2 +0,0 @@
pytest>=4.4
../../../[generator]

View File

@ -1,2 +0,0 @@
pytest>=4.4
../../../[generator]

View File

@ -1,2 +0,0 @@
pytest>=4.4
../../../[generator]

View File

@ -1,2 +0,0 @@
pytest>=4.4
../../../[generator]

View File

@ -1,2 +0,0 @@
pytest>=4.4
../../../[generator]

View File

@ -1,2 +0,0 @@
pytest>=4.4
../../../[generator]

View File

@ -1,2 +0,0 @@
pytest>=4.4
../../../[generator]

View File

@ -1,2 +0,0 @@
pytest>=4.4
../../../[generator]

View File

@ -1,2 +0,0 @@
pytest>=4.4
../../../[generator]

View File

@ -1,2 +0,0 @@
pytest>=4.4
../../../[generator]

View File

@ -1,2 +0,0 @@
pytest>=4.4
../../../[generator]

View File

@ -1,2 +0,0 @@
pytest>=4.4
../../../[generator]

View File

@ -1,2 +0,0 @@
pytest>=4.4
../../../[generator]