Skip to content

Commit

Permalink
Merge pull request ethereum#3775 from fradamt/EL-consolidations
Browse files Browse the repository at this point in the history
EL-triggered consolidations
  • Loading branch information
hwwhww committed Jun 4, 2024
2 parents 50972f9 + 143b9e6 commit 0de1252
Show file tree
Hide file tree
Showing 10 changed files with 887 additions and 934 deletions.
2 changes: 1 addition & 1 deletion presets/mainnet/electra.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ MAX_ATTESTER_SLASHINGS_ELECTRA: 1
# `uint64(2**3)` (= 8)
MAX_ATTESTATIONS_ELECTRA: 8
# `uint64(2**0)` (= 1)
MAX_CONSOLIDATIONS: 1
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD: 1

# Execution
# ---------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion presets/minimal/electra.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ MAX_ATTESTER_SLASHINGS_ELECTRA: 1
# `uint64(2**3)` (= 8)
MAX_ATTESTATIONS_ELECTRA: 8
# `uint64(2**0)` (= 1)
MAX_CONSOLIDATIONS: 1
MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD: 1

# Execution
# ---------------------------------------------------------------
Expand Down
124 changes: 68 additions & 56 deletions specs/electra/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@
- [`PendingBalanceDeposit`](#pendingbalancedeposit)
- [`PendingPartialWithdrawal`](#pendingpartialwithdrawal)
- [`ExecutionLayerWithdrawalRequest`](#executionlayerwithdrawalrequest)
- [`Consolidation`](#consolidation)
- [`SignedConsolidation`](#signedconsolidation)
- [`ExecutionLayerConsolidationRequest`](#executionlayerconsolidationrequest)
- [`PendingConsolidation`](#pendingconsolidation)
- [Modified Containers](#modified-containers)
- [`AttesterSlashing`](#attesterslashing)
Expand Down Expand Up @@ -95,8 +94,8 @@
- [New `process_execution_layer_withdrawal_request`](#new-process_execution_layer_withdrawal_request)
- [Deposit receipts](#deposit-receipts)
- [New `process_deposit_receipt`](#new-process_deposit_receipt)
- [Consolidations](#consolidations)
- [New `process_consolidation`](#new-process_consolidation)
- [Execution layer consolidation requests](#execution-layer-consolidation-requests)
- [New `process_execution_layer_consolidation_request`](#new-process_execution_layer_consolidation_request)
- [Testing](#testing)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->
Expand Down Expand Up @@ -165,16 +164,16 @@ The following values are (non-configurable) constants used throughout the specif

| Name | Value |
| - | - |
| `MAX_CONSOLIDATIONS` | `uint64(1)` |
| `MAX_ATTESTER_SLASHINGS_ELECTRA` | `2**0` (= 1) | *[New in Electra:EIP7549]* |
| `MAX_ATTESTATIONS_ELECTRA` | `2**3` (= 8) | *[New in Electra:EIP7549]* |

### Execution

| Name | Value | Description |
| - | - | - |
| `MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD` | `uint64(2**13)` (= 8,192) | *[New in Electra:EIP6110]* Maximum number of deposit receipts allowed in each payload |
| `MAX_ATTESTER_SLASHINGS_ELECTRA` | `2**0` (= 1) | *[New in Electra:EIP7549]* |
| `MAX_ATTESTATIONS_ELECTRA` | `2**3` (= 8) | *[New in Electra:EIP7549]* |
| `MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD` | `uint64(2**4)` (= 16)| *[New in Electra:EIP7002]* Maximum number of execution layer withdrawal requests in each payload |
| `MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD` | `uint64(1)` (= 1) | *[New in Electra:EIP7002]* Maximum number of execution layer consolidation requests in each payload |

### Withdrawals processing

Expand Down Expand Up @@ -239,25 +238,15 @@ class ExecutionLayerWithdrawalRequest(Container):
amount: Gwei
```

#### `Consolidation`

*Note*: The container is new in EIP7251.

```python
class Consolidation(Container):
source_index: ValidatorIndex
target_index: ValidatorIndex
epoch: Epoch
```

#### `SignedConsolidation`
#### `ExecutionLayerConsolidationRequest`

*Note*: The container is new in EIP7251.

```python
class SignedConsolidation(Container):
message: Consolidation
signature: BLSSignature
class ExecutionLayerConsolidationRequest(Container):
source_address: ExecutionAddress
source_pubkey: BLSPubkey
target_pubkey: BLSPubkey
```

#### `PendingConsolidation`
Expand Down Expand Up @@ -320,7 +309,6 @@ class BeaconBlockBody(Container):
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]
consolidations: List[SignedConsolidation, MAX_CONSOLIDATIONS] # [New in Electra:EIP7251]
```

#### `ExecutionPayload`
Expand Down Expand Up @@ -349,6 +337,8 @@ class ExecutionPayload(Container):
deposit_receipts: List[DepositReceipt, MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD] # [New in Electra:EIP6110]
# [New in Electra:EIP7002:EIP7251]
withdrawal_requests: List[ExecutionLayerWithdrawalRequest, MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD]
# [New in Electra:EIP7251]
consolidation_requests: List[ExecutionLayerConsolidationRequest, MAX_CONSOLIDATION_REQUESTS_PER_PAYLOAD]
```

#### `ExecutionPayloadHeader`
Expand Down Expand Up @@ -376,6 +366,7 @@ class ExecutionPayloadHeader(Container):
excess_blob_gas: uint64
deposit_receipts_root: Root # [New in Electra:EIP6110]
withdrawal_requests_root: Root # [New in Electra:EIP7002:EIP7251]
consolidation_requests_root: Root # [New in Electra:EIP7251]
```

#### `BeaconState`
Expand Down Expand Up @@ -1035,6 +1026,7 @@ def process_execution_payload(state: BeaconState, body: BeaconBlockBody, executi
excess_blob_gas=payload.excess_blob_gas,
deposit_receipts_root=hash_tree_root(payload.deposit_receipts), # [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]
)
```

Expand Down Expand Up @@ -1064,10 +1056,11 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
for_ops(body.deposits, process_deposit) # [Modified in Electra:EIP7251]
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_receipts, process_deposit_receipt) # [New in Electra:EIP6110]
# [New in Electra:EIP7002:EIP7251]
for_ops(body.execution_payload.withdrawal_requests, process_execution_layer_withdrawal_request)
for_ops(body.execution_payload.deposit_receipts, process_deposit_receipt) # [New in Electra:EIP6110]
for_ops(body.consolidations, process_consolidation) # [New in Electra:EIP7251]
# [New in Electra:EIP7251]
for_ops(body.execution_payload.consolidation_requests, process_execution_layer_consolidation_request)
```

##### Attestations
Expand Down Expand Up @@ -1314,43 +1307,62 @@ def process_deposit_receipt(state: BeaconState, deposit_receipt: DepositReceipt)
)
```

##### Consolidations
##### Execution layer consolidation requests

###### New `process_consolidation`
###### New `process_execution_layer_consolidation_request`

```python
def process_consolidation(state: BeaconState, signed_consolidation: SignedConsolidation) -> None:
# If the pending consolidations queue is full, no consolidations are allowed in the block
assert len(state.pending_consolidations) < PENDING_CONSOLIDATIONS_LIMIT
# If there is too little available consolidation churn limit, no consolidations are allowed in the block
assert get_consolidation_churn_limit(state) > MIN_ACTIVATION_BALANCE
consolidation = signed_consolidation.message
def process_execution_layer_consolidation_request(
state: BeaconState,
execution_layer_consolidation_request: ExecutionLayerConsolidationRequest
) -> None:
# If the pending consolidations queue is full, consolidation requests are ignored
if len(state.pending_consolidations) == PENDING_CONSOLIDATIONS_LIMIT:
return
# If there is too little available consolidation churn limit, consolidation requests are ignored
if get_consolidation_churn_limit(state) <= MIN_ACTIVATION_BALANCE:
return

validator_pubkeys = [v.pubkey for v in state.validators]
# Verify pubkeys exists
request_source_pubkey = execution_layer_consolidation_request.source_pubkey
request_target_pubkey = execution_layer_consolidation_request.target_pubkey
if request_source_pubkey not in validator_pubkeys:
return
if request_target_pubkey not in validator_pubkeys:
return
source_index = ValidatorIndex(validator_pubkeys.index(request_source_pubkey))
target_index = ValidatorIndex(validator_pubkeys.index(request_target_pubkey))
source_validator = state.validators[source_index]
target_validator = state.validators[target_index]

# Verify that source != target, so a consolidation cannot be used as an exit.
assert consolidation.source_index != consolidation.target_index
if source_index == target_index:
return

# Verify source withdrawal credentials
has_correct_credential = has_execution_withdrawal_credential(source_validator)
is_correct_source_address = (
source_validator.withdrawal_credentials[12:] == execution_layer_consolidation_request.source_address
)
if not (has_correct_credential and is_correct_source_address):
return

# Verify that target has execution withdrawal credentials
if not has_execution_withdrawal_credential(target_validator):
return

source_validator = state.validators[consolidation.source_index]
target_validator = state.validators[consolidation.target_index]
# Verify the source and the target are active
current_epoch = get_current_epoch(state)
assert is_active_validator(source_validator, current_epoch)
assert is_active_validator(target_validator, current_epoch)
if not is_active_validator(source_validator, current_epoch):
return
if not is_active_validator(target_validator, current_epoch):
return
# Verify exits for source and target have not been initiated
assert source_validator.exit_epoch == FAR_FUTURE_EPOCH
assert target_validator.exit_epoch == FAR_FUTURE_EPOCH
# Consolidations must specify an epoch when they become valid; they are not valid before then
assert current_epoch >= consolidation.epoch

# Verify the source and the target have Execution layer withdrawal credentials
assert has_execution_withdrawal_credential(source_validator)
assert has_execution_withdrawal_credential(target_validator)
# Verify the same withdrawal address
assert source_validator.withdrawal_credentials[12:] == target_validator.withdrawal_credentials[12:]

# Verify consolidation is signed by the source and the target
domain = compute_domain(DOMAIN_CONSOLIDATION, genesis_validators_root=state.genesis_validators_root)
signing_root = compute_signing_root(consolidation, domain)
pubkeys = [source_validator.pubkey, target_validator.pubkey]
assert bls.FastAggregateVerify(pubkeys, signing_root, signed_consolidation.signature)
if source_validator.exit_epoch != FAR_FUTURE_EPOCH:
return
if target_validator.exit_epoch != FAR_FUTURE_EPOCH:
return

# Initiate source validator exit and append pending consolidation
source_validator.exit_epoch = compute_consolidation_epoch_and_update_churn(
Expand All @@ -1360,8 +1372,8 @@ def process_consolidation(state: BeaconState, signed_consolidation: SignedConsol
source_validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY
)
state.pending_consolidations.append(PendingConsolidation(
source_index=consolidation.source_index,
target_index=consolidation.target_index
source_index=source_index,
target_index=target_index
))
```

Expand Down
3 changes: 2 additions & 1 deletion specs/electra/fork.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ def upgrade_to_electra(pre: deneb.BeaconState) -> BeaconState:
blob_gas_used=pre.latest_execution_payload_header.blob_gas_used,
excess_blob_gas=pre.latest_execution_payload_header.excess_blob_gas,
deposit_receipts_root=Root(), # [New in Electra:EIP6110]
withdrawal_requests_root=Root(), # [New in Electra:EIP7002],
withdrawal_requests_root=Root(), # [New in Electra:EIP7002]
consolidation_requests_root=Root(), # [New in Electra:EIP7251]
)

exit_epochs = [v.exit_epoch for v in pre.validators if v.exit_epoch != FAR_FUTURE_EPOCH]
Expand Down
Loading

0 comments on commit 0de1252

Please sign in to comment.