Merge pull request #1413 from ethereum/proposer-fix

Improve beacon proposer selection logic
This commit is contained in:
Danny Ryan 2019-09-23 13:45:43 -05:00 committed by GitHub
commit 114ffe3d6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 28 additions and 19 deletions

View File

@ -67,6 +67,7 @@
- [`is_valid_merkle_branch`](#is_valid_merkle_branch) - [`is_valid_merkle_branch`](#is_valid_merkle_branch)
- [Misc](#misc-1) - [Misc](#misc-1)
- [`compute_shuffled_index`](#compute_shuffled_index) - [`compute_shuffled_index`](#compute_shuffled_index)
- [`compute_proposer_index`](#compute_proposer_index)
- [`compute_committee`](#compute_committee) - [`compute_committee`](#compute_committee)
- [`compute_epoch_of_slot`](#compute_epoch_of_slot) - [`compute_epoch_of_slot`](#compute_epoch_of_slot)
- [`compute_start_slot_of_epoch`](#compute_start_slot_of_epoch) - [`compute_start_slot_of_epoch`](#compute_start_slot_of_epoch)
@ -717,6 +718,25 @@ def compute_shuffled_index(index: ValidatorIndex, index_count: uint64, seed: Has
return ValidatorIndex(index) return ValidatorIndex(index)
``` ```
#### `compute_proposer_index`
```python
def compute_proposer_index(state: BeaconState, indices: Sequence[ValidatorIndex], seed: Hash) -> ValidatorIndex:
"""
Return from ``indices`` a random index sampled by effective balance.
"""
assert len(indices) > 0
MAX_RANDOM_BYTE = 2**8 - 1
i = 0
while True:
candidate_index = indices[compute_shuffled_index(ValidatorIndex(i % len(indices)), len(indices), seed)]
random_byte = hash(seed + int_to_bytes(i // 32, length=8))[i % 32]
effective_balance = state.validators[candidate_index].effective_balance
if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte:
return ValidatorIndex(candidate_index)
i += 1
```
#### `compute_committee` #### `compute_committee`
```python ```python
@ -921,20 +941,9 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex:
Return the beacon proposer index at the current slot. Return the beacon proposer index at the current slot.
""" """
epoch = get_current_epoch(state) epoch = get_current_epoch(state)
committees_per_slot = get_committee_count(state, epoch) // SLOTS_PER_EPOCH seed = hash(get_seed(state, epoch) + int_to_bytes(state.slot, length=8))
offset = committees_per_slot * (state.slot % SLOTS_PER_EPOCH) indices = get_active_validator_indices(state, epoch)
shard = Shard((get_start_shard(state, epoch) + offset) % SHARD_COUNT) return compute_proposer_index(state, indices, seed)
first_committee = get_crosslink_committee(state, epoch, shard)
MAX_RANDOM_BYTE = 2**8 - 1
seed = get_seed(state, epoch)
i = 0
while True:
candidate_index = first_committee[(epoch + i) % len(first_committee)]
random_byte = hash(seed + int_to_bytes(i // 32, length=8))[i % 32]
effective_balance = state.validators[candidate_index].effective_balance
if effective_balance * MAX_RANDOM_BYTE >= MAX_EFFECTIVE_BALANCE * random_byte:
return ValidatorIndex(candidate_index)
i += 1
``` ```
#### `get_attestation_data_slot` #### `get_attestation_data_slot`
@ -1561,9 +1570,8 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
```python ```python
def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None: def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None:
proposer = state.validators[proposer_slashing.proposer_index] proposer = state.validators[proposer_slashing.proposer_index]
# Verify that the epoch is the same # Verify slots match
assert (compute_epoch_of_slot(proposer_slashing.header_1.slot) assert proposer_slashing.header_1.slot == proposer_slashing.header_2.slot
== compute_epoch_of_slot(proposer_slashing.header_2.slot))
# But the headers are different # But the headers are different
assert proposer_slashing.header_1 != proposer_slashing.header_2 assert proposer_slashing.header_1 != proposer_slashing.header_2
# Check proposer is slashable # Check proposer is slashable

View File

@ -160,7 +160,7 @@ def get_committee_assignment(state: BeaconState,
return None return None
``` ```
A validator can use the following function to see if they are supposed to propose during their assigned committee slot. This function can only be run with a `state` of the slot in question. Proposer selection is only stable within the context of the current epoch. A validator can use the following function to see if they are supposed to propose during a slot. This function can only be run with a `state` of the slot in question. Proposer selection is only stable within the context of the current epoch.
```python ```python
def is_proposer(state: BeaconState, def is_proposer(state: BeaconState,
@ -170,6 +170,8 @@ def is_proposer(state: BeaconState,
*Note*: To see if a validator is assigned to propose during the slot, the beacon state must be in the epoch in question. At the epoch boundaries, the validator must run an epoch transition into the epoch to successfully check the proposal assignment of the first slot. *Note*: To see if a validator is assigned to propose during the slot, the beacon state must be in the epoch in question. At the epoch boundaries, the validator must run an epoch transition into the epoch to successfully check the proposal assignment of the first slot.
*Note*: `BeaconBlock` proposal is distinct from crosslink committee assignment, and in a given epoch each responsibility might occur at different a different slot.
### Lookahead ### Lookahead
The beacon chain shufflings are designed to provide a minimum of 1 epoch lookahead on the validator's upcoming committee assignments for attesting dictated by the shuffling and slot. Note that this lookahead does not apply to proposing, which must be checked during the epoch in question. The beacon chain shufflings are designed to provide a minimum of 1 epoch lookahead on the validator's upcoming committee assignments for attesting dictated by the shuffling and slot. Note that this lookahead does not apply to proposing, which must be checked during the epoch in question.

View File

@ -18,7 +18,6 @@ def get_valid_proposer_slashing(spec, state, signed_1=False, signed_2=False):
) )
header_2 = deepcopy(header_1) header_2 = deepcopy(header_1)
header_2.parent_root = b'\x99' * 32 header_2.parent_root = b'\x99' * 32
header_2.slot = slot + 1
if signed_1: if signed_1:
sign_block_header(spec, state, header_1, privkey) sign_block_header(spec, state, header_1, privkey)