Merge pull request #1413 from ethereum/proposer-fix
Improve beacon proposer selection logic
This commit is contained in:
commit
114ffe3d6d
|
@ -67,6 +67,7 @@
|
|||
- [`is_valid_merkle_branch`](#is_valid_merkle_branch)
|
||||
- [Misc](#misc-1)
|
||||
- [`compute_shuffled_index`](#compute_shuffled_index)
|
||||
- [`compute_proposer_index`](#compute_proposer_index)
|
||||
- [`compute_committee`](#compute_committee)
|
||||
- [`compute_epoch_of_slot`](#compute_epoch_of_slot)
|
||||
- [`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)
|
||||
```
|
||||
|
||||
#### `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`
|
||||
|
||||
```python
|
||||
|
@ -921,20 +941,9 @@ def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex:
|
|||
Return the beacon proposer index at the current slot.
|
||||
"""
|
||||
epoch = get_current_epoch(state)
|
||||
committees_per_slot = get_committee_count(state, epoch) // SLOTS_PER_EPOCH
|
||||
offset = committees_per_slot * (state.slot % SLOTS_PER_EPOCH)
|
||||
shard = Shard((get_start_shard(state, epoch) + offset) % SHARD_COUNT)
|
||||
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
|
||||
seed = hash(get_seed(state, epoch) + int_to_bytes(state.slot, length=8))
|
||||
indices = get_active_validator_indices(state, epoch)
|
||||
return compute_proposer_index(state, indices, seed)
|
||||
```
|
||||
|
||||
#### `get_attestation_data_slot`
|
||||
|
@ -1561,9 +1570,8 @@ def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
|
|||
```python
|
||||
def process_proposer_slashing(state: BeaconState, proposer_slashing: ProposerSlashing) -> None:
|
||||
proposer = state.validators[proposer_slashing.proposer_index]
|
||||
# Verify that the epoch is the same
|
||||
assert (compute_epoch_of_slot(proposer_slashing.header_1.slot)
|
||||
== compute_epoch_of_slot(proposer_slashing.header_2.slot))
|
||||
# Verify slots match
|
||||
assert proposer_slashing.header_1.slot == proposer_slashing.header_2.slot
|
||||
# But the headers are different
|
||||
assert proposer_slashing.header_1 != proposer_slashing.header_2
|
||||
# Check proposer is slashable
|
||||
|
|
|
@ -160,7 +160,7 @@ def get_committee_assignment(state: BeaconState,
|
|||
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
|
||||
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*: `BeaconBlock` proposal is distinct from crosslink committee assignment, and in a given epoch each responsibility might occur at different a different slot.
|
||||
|
||||
### 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.
|
||||
|
|
|
@ -18,7 +18,6 @@ def get_valid_proposer_slashing(spec, state, signed_1=False, signed_2=False):
|
|||
)
|
||||
header_2 = deepcopy(header_1)
|
||||
header_2.parent_root = b'\x99' * 32
|
||||
header_2.slot = slot + 1
|
||||
|
||||
if signed_1:
|
||||
sign_block_header(spec, state, header_1, privkey)
|
||||
|
|
Loading…
Reference in New Issue