Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Address Comments and Cleanup Spec #4

Merged
merged 2 commits into from
Mar 20, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 135 additions & 75 deletions specs/_features/eip7251/beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
- [New `PendingConsolidation`](#new-pendingconsolidation)
- [Extended Containers](#extended-containers)
- [`BeaconState`](#beaconstate)
- [`BeaconBlockBody`](#beaconblockbody)
- [`BeaconBlockBody`](#beaconblockbody)
- [Helpers](#helpers)
- [Predicates](#predicates)
- [Updated `is_eligible_for_activation_queue`](#updated-is_eligible_for_activation_queue)
Expand All @@ -54,6 +54,8 @@
- [New `process_pending_consolidations`](#new-process_pending_consolidations)
- [Updated `process_effective_balance_updates`](#updated-process_effective_balance_updates)
- [Block processing](#block-processing)
- [Updated `get_expected_withdrawals`](#updated-get_expected_withdrawals)
- [Updated `process_withdrawals`](#updated-process_withdrawals)
- [Operations](#operations)
- [Updated `process_operations`](#updated-process_operations)
- [Deposits](#deposits)
Expand All @@ -62,7 +64,6 @@
- [Updated `get_validator_from_deposit`](#updated-get_validator_from_deposit)
- [Withdrawals](#withdrawals)
- [New `process_execution_layer_withdraw_request`](#new-process_execution_layer_withdraw_request)
- [Updated `get_expected_withdrawals`](#updated-get_expected_withdrawals)
- [Consolidations](#consolidations)
- [New `process_consolidation`](#new-process_consolidation)

Expand Down Expand Up @@ -117,11 +118,11 @@ The following values are (non-configurable) constants used throughout the specif

### State list lengths

| Name | Value | Unit | Duration |
| - | - | :-: | :-: |
| `PENDING_BALANCE_DEPOSITS_LIMIT` | `uint64(2**27)` (= 134,217,728) | pending balance deposits | |
| `PENDING_PARTIAL_WITHDRAWALS_LIMIT` | `uint64(2**27)` (= 134,217,728) | pending partial withdrawals | |
| `PENDING_CONSOLIDATIONS_LIMIT` | `uint64(2**18)` (= 262,144) | pending consolidations | |
| Name | Value | Unit |
| - | - | :-: |
| `PENDING_BALANCE_DEPOSITS_LIMIT` | `uint64(2**27)` (= 134,217,728) | pending balance deposits |
| `PENDING_PARTIAL_WITHDRAWALS_LIMIT` | `uint64(2**27)` (= 134,217,728) | pending partial withdrawals |
| `PENDING_CONSOLIDATIONS_LIMIT` | `uint64(2**18)` (= 262,144) | pending consolidations |


## Configuration
Expand Down Expand Up @@ -244,7 +245,7 @@ class BeaconState(Container):
pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT] # [New in EIP-7251]
```

### `BeaconBlockBody`
#### `BeaconBlockBody`

```python
class BeaconBlockBody(Container):
Expand Down Expand Up @@ -324,7 +325,7 @@ def is_partially_withdrawable_validator(validator: Validator, balance: Gwei) ->
Check if ``validator`` is partially withdrawable.
"""
return (
has_execution_withdrawal_credential(validator)
has_execution_withdrawal_credential(validator) # [Modified in EIP7251]
and get_validator_excess_balance(validator, balance) > 0
)
```
Expand Down Expand Up @@ -476,6 +477,15 @@ def slash_validator(state: BeaconState,
state.slashings[epoch % EPOCHS_PER_SLASHINGS_VECTOR] += validator.effective_balance
slashing_penalty = validator.effective_balance // MIN_SLASHING_PENALTY_QUOTIENT_EIP7251 # [Modified in EIP7251]
decrease_balance(state, slashed_index, slashing_penalty)

# Apply proposer and whistleblower rewards
proposer_index = get_beacon_proposer_index(state)
if whistleblower_index is None:
whistleblower_index = proposer_index
whistleblower_reward = Gwei(validator.effective_balance // WHISTLEBLOWER_REWARD_QUOTIENT)
proposer_reward = Gwei(whistleblower_reward * PROPOSER_WEIGHT // WEIGHT_DENOMINATOR)
increase_balance(state, proposer_index, proposer_reward)
increase_balance(state, whistleblower_index, Gwei(whistleblower_reward - proposer_reward))
```

## Beacon chain state transition function
Expand All @@ -488,12 +498,12 @@ def process_epoch(state: BeaconState) -> None:
process_justification_and_finalization(state)
process_inactivity_updates(state)
process_rewards_and_penalties(state)
process_registry_updates(state)
process_registry_updates(state) # [Modified in EIP7251]
process_slashings(state)
process_eth1_data_reset(state)
process_pending_balance_deposits(state) # New in EIP7251
process_pending_consolidations(state) # New in EIP7251
process_effective_balance_updates(state)
process_effective_balance_updates(state) # [Modified in EIP7251]
process_slashings_reset(state)
process_randao_mixes_reset(state)
```
Expand Down Expand Up @@ -523,17 +533,23 @@ def process_registry_updates(state: BeaconState) -> None:

```python
def process_pending_balance_deposits(state: BeaconState) -> None:
state.deposit_balance_to_consume += get_activation_exit_churn_limit(state)
next_pending_deposit_index = 0
for pending_balance_deposit in state.pending_balance_deposits:
if state.deposit_balance_to_consume < pending_balance_deposit.amount:
available_for_processing = state.deposit_balance_to_consume + get_activation_exit_churn_limit(state)
processed_amount = 0
next_deposit_index = 0

for deposit in state.pending_balance_deposits:
if processed_amount + deposit.amount > available_for_processing:
break
increase_balance(state, deposit.index, deposit.amount)
processed_amount += deposit.amount
next_deposit_index += 1

state.deposit_balance_to_consume -= pending_balance_deposit.amount
increase_balance(state, pending_balance_deposit.index, pending_balance_deposit.amount)
next_pending_deposit_index += 1
state.pending_balance_deposits = state.pending_balance_deposits[next_deposit_index:]

state.pending_balance_deposits = state.pending_balance_deposits[next_pending_deposit_index:]
if len(state.pending_balance_deposits) == 0:
state.deposit_balance_to_consume = 0
else:
state.deposit_balance_to_consume = available_for_processing - processed_amount
```

#### New `process_pending_consolidations`
Expand Down Expand Up @@ -582,6 +598,104 @@ def process_effective_balance_updates(state: BeaconState) -> None:

### Block processing

```python
def process_block(state: BeaconState, block: BeaconBlock) -> None:
process_block_header(state, block)
process_withdrawals(state, block.body.execution_payload) # [Modified in EIP7251]
process_execution_payload(state, block.body, EXECUTION_ENGINE)
process_randao(state, block.body)
process_eth1_data(state, block.body)
process_operations(state, block.body) # [Modified in EIP7251]
process_sync_aggregate(state, block.body.sync_aggregate)
```

##### Updated `get_expected_withdrawals`

```python
def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal], uint64]:
epoch = get_current_epoch(state)
withdrawal_index = state.next_withdrawal_index
validator_index = state.next_withdrawal_validator_index
withdrawals: List[Withdrawal] = []

# [New in EIP7251] Consume pending partial withdrawals
for withdrawal in state.pending_partial_withdrawals:
if withdrawal.withdrawable_epoch > epoch or len(withdrawals) == MAX_WITHDRAWALS_PER_PAYLOAD // 2:
break

validator = state.validators[withdrawal.index]
if validator.exit_epoch == FAR_FUTURE_EPOCH and state.balances[withdrawal.index] > MIN_ACTIVATION_BALANCE:
withdrawable_balance = min(state.balances[withdrawal.index] - MIN_ACTIVATION_BALANCE, withdrawal.amount)
withdrawals.append(Withdrawal(
index=withdrawal_index,
validator_index=withdrawal.index,
address=ExecutionAddress(validator.withdrawal_credentials[12:]),
amount=withdrawable_balance,
))
withdrawal_index += WithdrawalIndex(1)

partial_withdrawals_len = len(withdrawals)
# END: Consume pending partial withdrawals

# Sweep for remaining.
bound = min(len(state.validators), MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP)
for _ in range(bound):
validator = state.validators[validator_index]
balance = state.balances[validator_index]
if is_fully_withdrawable_validator(validator, balance, epoch):
withdrawals.append(Withdrawal(
index=withdrawal_index,
validator_index=validator_index,
address=ExecutionAddress(validator.withdrawal_credentials[12:]),
amount=balance,
))
withdrawal_index += WithdrawalIndex(1)
elif is_partially_withdrawable_validator(validator, balance):
withdrawals.append(Withdrawal(
index=withdrawal_index,
validator_index=validator_index,
address=ExecutionAddress(validator.withdrawal_credentials[12:]),
amount=get_validator_excess_balance(validator, balance), # [Modified in EIP7251]
))
withdrawal_index += WithdrawalIndex(1)
if len(withdrawals) == MAX_WITHDRAWALS_PER_PAYLOAD:
break
validator_index = ValidatorIndex((validator_index + 1) % len(state.validators))
return withdrawals, partial_withdrawals_len
```

##### Updated `process_withdrawals`

```python
def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
expected_withdrawals, partial_withdrawals_len = get_expected_withdrawals(state) # [Modified in EIP7251]

assert len(payload.withdrawals) == len(expected_withdrawals)

for expected_withdrawal, withdrawal in zip(expected_withdrawals, payload.withdrawals):
assert withdrawal == expected_withdrawal
decrease_balance(state, withdrawal.validator_index, withdrawal.amount)

# [New in EIP7251] update pending partial withdrawals
state.pending_partial_withdrawals = state.pending_partial_withdrawals[partial_withdrawals_len:]

# Update the next withdrawal index if this block contained withdrawals
if len(expected_withdrawals) != 0:
latest_withdrawal = expected_withdrawals[-1]
state.next_withdrawal_index = WithdrawalIndex(latest_withdrawal.index + 1)

# Update the next validator index to start the next withdrawal sweep
if len(expected_withdrawals) == MAX_WITHDRAWALS_PER_PAYLOAD:
# Next sweep starts after the latest withdrawal's validator index
next_validator_index = ValidatorIndex((expected_withdrawals[-1].validator_index + 1) % len(state.validators))
state.next_withdrawal_validator_index = next_validator_index
else:
# Advance sweep by the max length of the sweep if there was not a full set of withdrawals
next_index = state.next_withdrawal_validator_index + MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP
next_validator_index = ValidatorIndex(next_index % len(state.validators))
state.next_withdrawal_validator_index = next_validator_index
```

#### Operations

##### Updated `process_operations`
Expand All @@ -598,9 +712,9 @@ 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)
for_ops(body.deposits, process_deposit)
for_ops(body.deposits, process_deposit) # [Modified in EIP7251]
for_ops(body.voluntary_exits, process_voluntary_exit)
for_ops(body.bls_to_execution_changes, process_bls_to_execution_change)
for_ops(body.bls_to_execution_changes, process_bls_to_execution_change)
for_ops(body.execution_payload.withdraw_requests, process_execution_layer_withdraw_request) # New in EIP7251
for_ops(body.consolidations, process_consolidation) # New in EIP7251
```
Expand Down Expand Up @@ -721,60 +835,6 @@ def process_execution_layer_withdraw_request(
))
```

###### Updated `get_expected_withdrawals`

```python
def get_expected_withdrawals(state: BeaconState) -> Sequence[Withdrawal]:
epoch = get_current_epoch(state)
withdrawal_index = state.next_withdrawal_index
validator_index = state.next_withdrawal_validator_index
withdrawals: List[Withdrawal] = []
consumed = 0
for withdrawal in state.pending_partial_withdrawals:
if withdrawal.withdrawable_epoch > epoch or len(withdrawals) == MAX_WITHDRAWALS_PER_PAYLOAD // 2:
break

validator = state.validators[withdrawal.index]
if validator.exit_epoch == FAR_FUTURE_EPOCH and state.balances[withdrawal.index] > MIN_ACTIVATION_BALANCE:
withdrawable_balance = min(state.balances[withdrawal.index] - MIN_ACTIVATION_BALANCE, withdrawal.amount)
withdrawals.append(Withdrawal(
index=withdrawal_index,
validator_index=withdrawal.index,
address=ExecutionAddress(validator.withdrawal_credentials[12:]),
amount=withdrawable_balance,
))
withdrawal_index += WithdrawalIndex(1)
consumed += 1

state.pending_partial_withdrawals = state.pending_partial_withdrawals[consumed:]

# Sweep for remaining.
bound = min(len(state.validators), MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP)
for _ in range(bound):
validator = state.validators[validator_index]
balance = state.balances[validator_index]
if is_fully_withdrawable_validator(validator, balance, epoch):
withdrawals.append(Withdrawal(
index=withdrawal_index,
validator_index=validator_index,
address=ExecutionAddress(validator.withdrawal_credentials[12:]),
amount=balance,
))
withdrawal_index += WithdrawalIndex(1)
elif is_partially_withdrawable_validator(validator, balance):
withdrawals.append(Withdrawal(
index=withdrawal_index,
validator_index=validator_index,
address=ExecutionAddress(validator.withdrawal_credentials[12:]),
amount=get_validator_excess_balance(validator, balance),
))
withdrawal_index += WithdrawalIndex(1)
if len(withdrawals) == MAX_WITHDRAWALS_PER_PAYLOAD:
break
validator_index = ValidatorIndex((validator_index + 1) % len(state.validators))
return withdrawals
```

##### Consolidations

###### New `process_consolidation`
Expand Down