-
Notifications
You must be signed in to change notification settings - Fork 997
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
EIP-6988: Slashed validator cannot be elected as a block proposer #3371
Open
mkalinin
wants to merge
18
commits into
ethereum:dev
Choose a base branch
from
mkalinin:eip6988
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 15 commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
cef5e7c
Add eip6988
mkalinin f47defb
Update tests with eip6988
mkalinin 28628b8
Enable eip6988 on CI
mkalinin 4da3fe0
Add comment about zero elements in batch verification (#3367)
jtraglia 8c5777c
Fix CircleCI Python version
hwwhww 1f5e17d
Nitpick: blob -> blob sidecar
terencechain 974019c
Fix image version
hwwhww 405391e
Refactor `run_generator`
hwwhww 4b0a7d2
Try multiprocessing
hwwhww 620ac52
Fix and set to `MODE_MULTIPROCESSING`
hwwhww b6ee834
Add `settings.py` of testgen
hwwhww c6adef9
Specify the number of sidecar subnets
ppopth b9b2d5e
Use SubnetID instead of uint64
ppopth 0a09f79
Clarify blob subnets
ralexstokes 0ad3972
Merge branch 'dev' into eip6988
mkalinin 4bdc78f
Fix proposer election tests
mkalinin 8740967
Add sanity block tests
mkalinin d60ae65
Add more slashing tests
mkalinin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
# EIP-6988 -- The Beacon Chain | ||
|
||
## Table of contents | ||
|
||
<!-- TOC --> | ||
<!-- START doctoc generated TOC please keep comment here to allow auto update --> | ||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> | ||
|
||
- [Introduction](#introduction) | ||
- [Helper functions](#helper-functions) | ||
- [Misc](#misc) | ||
- [Modified `compute_proposer_index`](#modified-compute_proposer_index) | ||
- [Modified `get_beacon_proposer_index`](#modified-get_beacon_proposer_index) | ||
- [Testing](#testing) | ||
|
||
<!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
<!-- /TOC --> | ||
|
||
## Introduction | ||
|
||
This is the beacon chain specification of [EIP-6988](https://github.com/ethereum/EIPs/pull/6988). | ||
EIP-6988 modifies the protocol to prevent slashed validator from becoming a block proposer. | ||
|
||
*Note:* This specification is built upon [Capella](../../capella/beacon-chain.md) and is under active development. | ||
|
||
## Helper functions | ||
|
||
### Misc | ||
|
||
#### Modified `compute_proposer_index` | ||
|
||
*Note:* Disallowing slashed validator to become a proposer is the only modification of this function. | ||
|
||
```python | ||
def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex], seed: Bytes32) -> ValidatorIndex: | ||
""" | ||
Return from ``indices`` a random index sampled by effective balance. | ||
""" | ||
assert len(indices) > 0 | ||
MAX_RANDOM_BYTE = 2**8 - 1 | ||
i = uint64(0) | ||
total = uint64(len(indices)) | ||
while True: | ||
candidate_index = indices[compute_shuffled_index(i % total, total, seed)] | ||
random_byte = hash(seed + uint_to_bytes(uint64(i // 32)))[i % 32] | ||
effective_balance = state.validators[candidate_index].effective_balance | ||
# [Modified in EIP6988] | ||
slashed = state.validators[candidate_index].slashed | ||
if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte and not slashed: | ||
return candidate_index | ||
i += 1 | ||
``` | ||
|
||
#### Modified `get_beacon_proposer_index` | ||
|
||
*Note:* Modified to read proposer index from the state to ensure the output stays unaffected if proposing validator gets slashed. | ||
|
||
```python | ||
def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex: | ||
""" | ||
Return the beacon proposer index at the current slot. | ||
""" | ||
# [New in EIP6988] | ||
if state.latest_block_header.slot == state.slot: | ||
return state.latest_block_header.proposer_index | ||
|
||
epoch = get_current_epoch(state) | ||
seed = hash(get_seed(state, epoch, DOMAIN_BEACON_PROPOSER) + uint_to_bytes(state.slot)) | ||
indices = get_active_validator_indices(state, epoch) | ||
return compute_proposer_index(state, indices, seed) | ||
``` | ||
|
||
## Testing | ||
|
||
*Note*: The function `initialize_beacon_state_from_eth1` is modified for pure EIP-6988 testing only. | ||
Modifications include: | ||
1. Use `EIP6988_FORK_VERSION` as the previous and current fork version. | ||
2. Utilize the EIP-6988 `BeaconBlockBody` when constructing the initial `latest_block_header`. | ||
3. Add `deposit_receipts_start_index` variable to the genesis state initialization. | ||
|
||
```python | ||
def initialize_beacon_state_from_eth1(eth1_block_hash: Hash32, | ||
eth1_timestamp: uint64, | ||
deposits: Sequence[Deposit], | ||
execution_payload_header: ExecutionPayloadHeader=ExecutionPayloadHeader() | ||
) -> BeaconState: | ||
fork = Fork( | ||
previous_version=EIP6988_FORK_VERSION, # [Modified in EIP6988] for testing only | ||
current_version=EIP6988_FORK_VERSION, # [Modified in EIP6988] | ||
epoch=GENESIS_EPOCH, | ||
) | ||
state = BeaconState( | ||
genesis_time=eth1_timestamp + GENESIS_DELAY, | ||
fork=fork, | ||
eth1_data=Eth1Data(block_hash=eth1_block_hash, deposit_count=uint64(len(deposits))), | ||
latest_block_header=BeaconBlockHeader(body_root=hash_tree_root(BeaconBlockBody())), | ||
randao_mixes=[eth1_block_hash] * EPOCHS_PER_HISTORICAL_VECTOR, # Seed RANDAO with Eth1 entropy | ||
) | ||
|
||
# Process deposits | ||
leaves = list(map(lambda deposit: deposit.data, deposits)) | ||
for index, deposit in enumerate(deposits): | ||
deposit_data_list = List[DepositData, 2**DEPOSIT_CONTRACT_TREE_DEPTH](*leaves[:index + 1]) | ||
state.eth1_data.deposit_root = hash_tree_root(deposit_data_list) | ||
process_deposit(state, deposit) | ||
|
||
# Process activations | ||
for index, validator in enumerate(state.validators): | ||
balance = state.balances[index] | ||
validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, MAX_EFFECTIVE_BALANCE) | ||
if validator.effective_balance == MAX_EFFECTIVE_BALANCE: | ||
validator.activation_eligibility_epoch = GENESIS_EPOCH | ||
validator.activation_epoch = GENESIS_EPOCH | ||
|
||
# Set genesis validators root for domain separation and chain versioning | ||
state.genesis_validators_root = hash_tree_root(state.validators) | ||
|
||
# Fill in sync committees | ||
# Note: A duplicate committee is assigned for the current and next committee at genesis | ||
state.current_sync_committee = get_next_sync_committee(state) | ||
state.next_sync_committee = get_next_sync_committee(state) | ||
|
||
# Initialize the execution payload header | ||
state.latest_execution_payload_header = execution_payload_header | ||
|
||
return state | ||
``` |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This actually has a strange side effect --- the shuffling for an epoch is no longer fixed at the start of the epoch as a slashing could remove a validator from a previously selected slot and replace them with someone else.
This isn't a deal breaker but likely breaks engineering invariants. We need to discuss with engineers and make sure to have specific tests for this (i.e. predict the epoch's shuffling, then move a coupel of blocks forward with a slashing for the next slot proposer, then show that the proposer changes)
EDIT: this is directionally the test --
test_slashed_validator_is_not_proposing_block
-- but I'd want it to be a multi-block test to ensure some epoch proposer shuffling cache doesn't become an issue. That is, have a block that does the transition into the epoch, have a block that adds the slashing, then have the block of the next slot that has the changed proposerThis might also likely have interactions with VC proposer duties API which might expect epoch stability. It might actulaly ripple into a bunch of small things like that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very good point! 👍
Strictly, the shuffling for an epoch is not fixed before this change was applied, and in the status quo slashing can become a reason of a proposer switch because of its economics component. This change moves this effect from probabilistic to deterministic area and because of that it should be taken with care.EDIT: the shuffling for an epoch is actually fixed because
effective_balance
is updated only during epoch processing, thus, balance decrease because of slashing takes into effect only at the end of the epochThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added
test_proposer_shuffling_changes_within_epoch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but it is due to
effective_balance
only being updated at epoch boundaries! let's discuss on call todayThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could compute the proposers in the epoch transition and persist the result in the state. Citing the EIP motivation
This PR reduces the impact to 0 slots of potential missed proposes. With my suggestion it would be reduced to max of 32 slots.
At least in Lodestar and probably in all clients, breaking dependant_root patterns will have a significant impact on the validator client's design.
EDIT: significant in time consuming but not a breaking deal. I can try to quantify it when there's bandwidth. But definitely the EIP will require more dev work than I originally imagined
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've attempted to spec out proposer shuffling in state #3405. It doesn't seem like a big change after all.
This modification requires beacon state initialization to be finished with a call to
update_proposer_shuffling
like it is done ininitialize_beacon_state_from_eth1
andupgrade_to_6988
. I am wondering about other implication that this requirement can have.