attesation proposer rewards to block processing (#920)

This commit is contained in:
Danny Ryan 2019-04-17 12:32:50 +10:00 committed by Justin
parent c82acc6970
commit 882937b537
8 changed files with 73 additions and 83 deletions

View File

@ -39,7 +39,7 @@ def run_attester_slashing_processing(state, attester_slashing, valid=True):
get_balance(post_state, slashed_index) <
get_balance(state, slashed_index)
)
proposer_index = get_beacon_proposer_index(state, state.slot)
proposer_index = get_beacon_proposer_index(state)
# gained whistleblower reward
assert (
get_balance(post_state, proposer_index) >

View File

@ -10,6 +10,7 @@ from eth2spec.phase0.spec import (
)
from phase0.helpers import (
build_empty_block_for_next_slot,
next_slot,
)
# mark entire file as 'header'
@ -61,8 +62,12 @@ def test_invalid_previous_block_root(state):
def test_proposer_slashed(state):
# use stub state to get proposer index of next slot
stub_state = deepcopy(state)
next_slot(stub_state)
proposer_index = get_beacon_proposer_index(stub_state)
# set proposer to slashed
proposer_index = get_beacon_proposer_index(state, state.slot + 1)
state.validator_registry[proposer_index].slashed = True
block = build_empty_block_for_next_slot(state)

View File

@ -2,6 +2,9 @@ from copy import deepcopy
from py_ecc import bls
from eth2spec.phase0.state_transition import (
state_transition,
)
import eth2spec.phase0.spec as spec
from eth2spec.utils.minimal_ssz import signing_root
from eth2spec.phase0.spec import (
@ -303,3 +306,8 @@ def get_attestation_signature(state, attestation_data, privkey, custody_bit=0b0)
domain_type=spec.DOMAIN_ATTESTATION,
)
)
def next_slot(state):
block = build_empty_block_for_next_slot(state)
state_transition(state, block)

View File

@ -160,7 +160,7 @@ def test_attester_slashing(state):
# lost whistleblower reward
assert get_balance(test_state, validator_index) < get_balance(state, validator_index)
proposer_index = get_beacon_proposer_index(test_state, test_state.slot)
proposer_index = get_beacon_proposer_index(test_state)
# gained whistleblower reward
assert (
get_balance(test_state, proposer_index) >
@ -260,6 +260,9 @@ def test_attestation(state):
assert len(test_state.current_epoch_attestations) == len(state.current_epoch_attestations) + 1
proposer_index = get_beacon_proposer_index(test_state)
assert test_state.balances[proposer_index] > state.balances[proposer_index]
#
# Epoch transition should move to previous_epoch_attestations
#

View File

@ -984,25 +984,17 @@ def generate_seed(state: BeaconState,
### `get_beacon_proposer_index`
```python
def get_beacon_proposer_index(state: BeaconState,
slot: Slot) -> ValidatorIndex:
def get_beacon_proposer_index(state: BeaconState) -> ValidatorIndex:
"""
Return the beacon proposer index for the ``slot``.
Due to proposer selection being based upon the validator balances during
the epoch in question, this can only be run for the current epoch.
Return the beacon proposer index at ``state.slot``.
"""
current_epoch = get_current_epoch(state)
assert slot_to_epoch(slot) == current_epoch
first_committee, _ = get_crosslink_committees_at_slot(state, slot)[0]
first_committee, _ = get_crosslink_committees_at_slot(state, state.slot)[0]
i = 0
while True:
rand_byte = hash(
generate_seed(state, current_epoch) +
int_to_bytes8(i // 32)
)[i % 32]
candidate = first_committee[(current_epoch + i) % len(first_committee)]
if get_effective_balance(state, candidate) * 256 > MAX_DEPOSIT_AMOUNT * rand_byte:
random_byte = hash(generate_seed(state, current_epoch) + int_to_bytes8(i // 32))[i % 32]
if get_effective_balance(state, candidate) * 256 > MAX_DEPOSIT_AMOUNT * random_byte:
return candidate
i += 1
```
@ -1051,16 +1043,8 @@ def get_attestation_participants(state: BeaconState,
Return the sorted participant indices corresponding to ``attestation_data`` and ``bitfield``.
"""
crosslink_committee = get_crosslink_committee_for_attestation(state, attestation_data)
assert verify_bitfield(bitfield, len(crosslink_committee))
# Find the participating attesters in the committee
participants = []
for i, validator_index in enumerate(crosslink_committee):
aggregation_bit = get_bitfield_bit(bitfield, i)
if aggregation_bit == 0b1:
participants.append(validator_index)
return sorted(participants)
return sorted([index for i, index in enumerate(crosslink_committee) if get_bitfield_bit(bitfield, i) == 0b1])
```
### `int_to_bytes1`, `int_to_bytes2`, ...
@ -1344,7 +1328,7 @@ def slash_validator(state: BeaconState, slashed_index: ValidatorIndex, whistlebl
slashed_balance = get_effective_balance(state, slashed_index)
state.latest_slashed_balances[get_current_epoch(state) % LATEST_SLASHED_EXIT_LENGTH] += slashed_balance
proposer_index = get_beacon_proposer_index(state, state.slot)
proposer_index = get_beacon_proposer_index(state)
if whistleblower_index is None:
whistleblower_index = proposer_index
whistleblowing_reward = slashed_balance // WHISTLEBLOWING_REWARD_QUOTIENT
@ -1494,9 +1478,9 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit],
process_deposit(state, deposit)
# Process genesis activations
for validator_index in range(len(state.validator_registry)):
if get_effective_balance(state, validator_index) >= MAX_DEPOSIT_AMOUNT:
activate_validator(state, validator_index, is_genesis=True)
for index in range(len(state.validator_registry)):
if get_effective_balance(state, index) >= MAX_DEPOSIT_AMOUNT:
activate_validator(state, index, is_genesis=True)
genesis_active_index_root = hash_tree_root(get_active_validator_indices(state, GENESIS_EPOCH))
for index in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH):
@ -1547,8 +1531,8 @@ def get_ancestor(store: Store, block: BeaconBlock, slot: Slot) -> BeaconBlock:
return get_ancestor(store, store.get_parent(block), slot)
```
* Let `get_latest_attestation(store: Store, validator_index: ValidatorIndex) -> Attestation` be the attestation with the highest slot number in `store` from the validator with the given `validator_index`. If several such attestations exist, use the one the [validator](#dfn-validator) `v` observed first.
* Let `get_latest_attestation_target(store: Store, validator_index: ValidatorIndex) -> BeaconBlock` be the target block in the attestation `get_latest_attestation(store, validator_index)`.
* Let `get_latest_attestation(store: Store, index: ValidatorIndex) -> Attestation` be the attestation with the highest slot number in `store` from the validator with the given `index`. If several such attestations exist, use the one the [validator](#dfn-validator) `v` observed first.
* Let `get_latest_attestation_target(store: Store, index: ValidatorIndex) -> BeaconBlock` be the target block in the attestation `get_latest_attestation(store, index)`.
* Let `get_children(store: Store, block: BeaconBlock) -> List[BeaconBlock]` returns the child blocks of the given `block`.
* Let `justified_head_state` be the resulting `BeaconState` object from processing the chain up to the `justified_head`.
* The `head` is `lmd_ghost(store, justified_head_state, justified_head)` where the function `lmd_ghost` is defined below. Note that the implementation below is suboptimal; there are implementations that compute the head in time logarithmic in slot count.
@ -1560,10 +1544,7 @@ def lmd_ghost(store: Store, start_state: BeaconState, start_block: BeaconBlock)
"""
validators = start_state.validator_registry
active_validator_indices = get_active_validator_indices(validators, slot_to_epoch(start_state.slot))
attestation_targets = [
(validator_index, get_latest_attestation_target(store, validator_index))
for validator_index in active_validator_indices
]
attestation_targets = [(i, get_latest_attestation_target(store, i)) for i in active_validator_indices]
# Use the rounded-balance-with-hysteresis supplied by the protocol for fork
# choice voting. This reduces the number of recomputations that need to be
@ -1628,7 +1609,7 @@ The steps below happen when `state.slot > GENESIS_SLOT and (state.slot + 1) % SL
#### Helper functions
We define some helper functions utilized when processing an epoch transition:
We define epoch transition helper functions:
```python
def get_current_total_balance(state: BeaconState) -> Gwei:
@ -1702,24 +1683,12 @@ def get_winning_root_and_participants(state: BeaconState, shard: Shard) -> Tuple
```
```python
def earliest_attestation(state: BeaconState, validator_index: ValidatorIndex) -> PendingAttestation:
def earliest_attestation(state: BeaconState, attestations: List[PendingAttestation], index: ValidatorIndex) -> PendingAttestation:
return min([
a for a in state.previous_epoch_attestations if
validator_index in get_attestation_participants(state, a.data, a.aggregation_bitfield)
a for a in attestations if index in get_attestation_participants(state, a.data, a.aggregation_bitfield)
], key=lambda a: a.inclusion_slot)
```
```python
def inclusion_slot(state: BeaconState, validator_index: ValidatorIndex) -> Slot:
return earliest_attestation(state, validator_index).inclusion_slot
```
```python
def inclusion_distance(state: BeaconState, validator_index: ValidatorIndex) -> int:
attestation = earliest_attestation(state, validator_index)
return attestation.inclusion_slot - attestation.data.slot
```
#### Justification
Run the following function:
@ -1805,14 +1774,19 @@ def maybe_reset_eth1_period(state: BeaconState) -> None:
First, we define some additional helpers:
```python
def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
if get_previous_total_balance(state) == 0:
def get_base_reward_from_total_balance(state: BeaconState, total_balance: Gwei, index: ValidatorIndex) -> Gwei:
if total_balance == 0:
return 0
adjusted_quotient = integer_squareroot(get_previous_total_balance(state)) // BASE_REWARD_QUOTIENT
adjusted_quotient = integer_squareroot(total_balance) // BASE_REWARD_QUOTIENT
return get_effective_balance(state, index) // adjusted_quotient // 5
```
```python
def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
return get_base_reward_from_total_balance(state, get_previous_total_balance(state), index)
```
```python
def get_inactivity_penalty(state: BeaconState, index: ValidatorIndex, epochs_since_finality: int) -> Gwei:
if epochs_since_finality <= 4:
@ -1853,10 +1827,9 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[
if index in get_unslashed_attesting_indices(state, state.previous_epoch_attestations):
rewards[index] += base_reward * total_attesting_balance // total_balance
# Inclusion speed bonus
rewards[index] += (
base_reward * MIN_ATTESTATION_INCLUSION_DELAY //
inclusion_distance(state, index)
)
earliest_attestation = earliest_attestation(state, state.previous_epoch_attestations, index)
inclusion_delay = earliest_attestation.inclusion_slot - earliest_attestation.data.slot
rewards[index] += base_reward * MIN_ATTESTATION_INCLUSION_DELAY // inclusion_delay
else:
penalties[index] += base_reward
# Expected FFG target
@ -1869,10 +1842,6 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[
rewards[index] += base_reward * matching_head_balance // total_balance
else:
penalties[index] += base_reward
# Proposer bonus
if index in get_unslashed_attesting_indices(state, state.previous_epoch_attestations):
proposer_index = get_beacon_proposer_index(state, inclusion_slot(state, index))
rewards[proposer_index] += base_reward // PROPOSER_REWARD_QUOTIENT
# Take away max rewards if we're not finalizing
if epochs_since_finality > 4:
penalties[index] += base_reward * 4
@ -1909,14 +1878,8 @@ def apply_rewards(state: BeaconState) -> None:
rewards1, penalties1 = get_justification_and_finalization_deltas(state)
rewards2, penalties2 = get_crosslink_deltas(state)
for i in range(len(state.validator_registry)):
set_balance(
state,
i,
max(
0,
get_balance(state, i) + rewards1[i] + rewards2[i] - penalties1[i] - penalties2[i],
),
)
increase_balance(state, i, rewards1[i] + rewards2[i])
decrease_balance(state, i, penalties1[i] + penalties2[i])
```
#### Balance-driven status transitions
@ -2042,7 +2005,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
# Save current block as the new latest block
state.latest_block_header = get_temporary_block_header(block)
# Verify proposer is not slashed
proposer = state.validator_registry[get_beacon_proposer_index(state, state.slot)]
proposer = state.validator_registry[get_beacon_proposer_index(state)]
assert not proposer.slashed
# Verify proposer signature
assert bls_verify(
@ -2057,7 +2020,7 @@ def process_block_header(state: BeaconState, block: BeaconBlock) -> None:
```python
def process_randao(state: BeaconState, block: BeaconBlock) -> None:
proposer = state.validator_registry[get_beacon_proposer_index(state, state.slot)]
proposer = state.validator_registry[get_beacon_proposer_index(state)]
# Verify that the provided randao value is valid
assert bls_verify(
pubkey=proposer.pubkey,
@ -2205,6 +2168,17 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
state.previous_epoch_attestations.append(pending_attestation)
```
Run `process_proposer_attestation_rewards(state)`.
```python
def process_proposer_attestation_rewards(state: BeaconState) -> None:
for pending_attestations in (state.previous_epoch_attestations, state.current_epoch_attestations):
for index in get_unslashed_attesting_indices(state, pending_attestations):
if earliest_attestation(state, pending_attestations, index).inclusion_slot == state.slot:
base_reward = get_base_reward_from_total_balance(state, get_current_total_balance(state), index)
increase_balance(state, get_beacon_proposer_index(state), base_reward // PROPOSER_REWARD_QUOTIENT)
```
##### Deposits
Verify that `len(block.body.deposits) == min(MAX_DEPOSITS, latest_eth1_data.deposit_count - state.deposit_index)`.
@ -2350,7 +2324,7 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None:
# Process the transfer
decrease_balance(state, transfer.sender, transfer.amount + transfer.fee)
increase_balance(state, transfer.recipient, transfer.amount)
increase_balance(state, get_beacon_proposer_index(state, state.slot), transfer.fee)
increase_balance(state, get_beacon_proposer_index(state), transfer.fee)
# Verify balances are not dust
assert not (0 < get_balance(state, transfer.sender) < MIN_DEPOSIT_AMOUNT)
assert not (0 < get_balance(state, transfer.recipient) < MIN_DEPOSIT_AMOUNT)

View File

@ -283,7 +283,7 @@ def process_custody_reveal(state: BeaconState,
assert is_active_validator(revealer, get_current_epoch(state)) or revealer.exit_epoch > get_current_epoch(state)
revealer.custody_reveal_index += 1
revealer.max_reveal_lateness = max(revealer.max_reveal_lateness, current_custody_period - reveal.period)
proposer_index = get_beacon_proposer_index(state, state.slot)
proposer_index = get_beacon_proposer_index(state)
increase_balance(state, proposer_index, base_reward(state, index) // MINOR_REWARD_QUOTIENT)
# Case 2: masked punitive early reveal
@ -323,7 +323,7 @@ def process_chunk_challenge(state: BeaconState,
# Add new chunk challenge record
state.custody_chunk_challenge_records.append(CustodyChunkChallengeRecord(
challenge_index=state.custody_challenge_index,
challenger_index=get_beacon_proposer_index(state, state.slot),
challenger_index=get_beacon_proposer_index(state),
responder_index=challenge.responder_index
deadline=get_current_epoch(state) + CUSTODY_RESPONSE_DEADLINE,
crosslink_data_root=challenge.attestation.data.crosslink_data_root,
@ -436,7 +436,7 @@ def process_chunk_challenge_response(state: BeaconState,
# Clear the challenge
state.custody_chunk_challenge_records.remove(challenge)
# Reward the proposer
proposer_index = get_beacon_proposer_index(state, state.slot)
proposer_index = get_beacon_proposer_index(state)
increase_balance(state, proposer_index, base_reward(state, index) // MINOR_REWARD_QUOTIENT)
```

View File

@ -369,24 +369,23 @@ def get_committee_assignment(
return assignment
```
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 during the epoch of the slot in question and can not reliably be used to predict an epoch in advance.
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 during the slot in question and can not reliably be used to predict in advance.
```python
def is_proposer_at_slot(state: BeaconState,
slot: Slot,
validator_index: ValidatorIndex) -> bool:
current_epoch = get_current_epoch(state)
assert slot_to_epoch(slot) == current_epoch
assert state.slot == slot
return get_beacon_proposer_index(state, slot) == validator_index
return get_beacon_proposer_index(state) == validator_index
```
_Note_: If a validator is assigned to the 0th slot of an epoch, the validator must run an empty slot transition from the previous epoch into the 0th slot of the epoch to be able to check if they are a proposer at that slot.
_Note_: To see if a validator is assigned to proposer during the slot, the validator must run an empty slot transition from the previous state to the current 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 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 checked during the slot in question.
`get_committee_assignment` should be called at the start of each epoch to get the assignment for the next epoch (`current_epoch + 1`). A validator should plan for future assignments which involves noting at which future slot one will have to attest and also which shard one should begin syncing (in phase 1+).

View File

@ -10,7 +10,8 @@ from typing import (
from .spec import (
BeaconState,
BeaconBlock,
Slot
Slot,
process_proposer_attestation_rewards,
)
@ -51,6 +52,7 @@ def process_operations(state: BeaconState, block: BeaconBlock) -> None:
spec.MAX_ATTESTATIONS,
spec.process_attestation,
)
process_proposer_attestation_rewards(state)
assert len(block.body.deposits) == expected_deposit_count(state)
process_operation_type(
@ -112,4 +114,3 @@ def state_transition(state: BeaconState,
verify_state_root: bool=False) -> BeaconState:
state_transition_to(state, block.slot)
process_block(state, block, verify_state_root)