mirror of
https://github.com/status-im/eth2.0-specs.git
synced 2025-01-13 04:04:19 +00:00
Merge pull request #2222 from ethereum/leak-scores
independent leak score feature
This commit is contained in:
commit
ea9f3f184f
@ -18,6 +18,8 @@ ALTAIR_PROPORTIONAL_SLASHING_MULTIPLIER: 2
|
|||||||
SYNC_COMMITTEE_SIZE: 1024
|
SYNC_COMMITTEE_SIZE: 1024
|
||||||
# 2**6 (=64)
|
# 2**6 (=64)
|
||||||
SYNC_SUBCOMMITTEE_SIZE: 64
|
SYNC_SUBCOMMITTEE_SIZE: 64
|
||||||
|
# 2**2 (=4)
|
||||||
|
LEAK_SCORE_BIAS: 4
|
||||||
|
|
||||||
|
|
||||||
# Time parameters
|
# Time parameters
|
||||||
|
@ -18,6 +18,8 @@ ALTAIR_PROPORTIONAL_SLASHING_MULTIPLIER: 2
|
|||||||
SYNC_COMMITTEE_SIZE: 32
|
SYNC_COMMITTEE_SIZE: 32
|
||||||
# [customized]
|
# [customized]
|
||||||
SYNC_SUBCOMMITTEE_SIZE: 16
|
SYNC_SUBCOMMITTEE_SIZE: 16
|
||||||
|
# 2**2 (=4)
|
||||||
|
LEAK_SCORE_BIAS: 4
|
||||||
|
|
||||||
|
|
||||||
# Time parameters
|
# Time parameters
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
- [Time parameters](#time-parameters)
|
- [Time parameters](#time-parameters)
|
||||||
- [Domain types](#domain-types)
|
- [Domain types](#domain-types)
|
||||||
- [Containers](#containers)
|
- [Containers](#containers)
|
||||||
- [Extended containers](#extended-containers)
|
- [Modified containers](#modified-containers)
|
||||||
- [`BeaconBlockBody`](#beaconblockbody)
|
- [`BeaconBlockBody`](#beaconblockbody)
|
||||||
- [`BeaconState`](#beaconstate)
|
- [`BeaconState`](#beaconstate)
|
||||||
- [New containers](#new-containers)
|
- [New containers](#new-containers)
|
||||||
@ -41,10 +41,11 @@
|
|||||||
- [New `slash_validator`](#new-slash_validator)
|
- [New `slash_validator`](#new-slash_validator)
|
||||||
- [Block processing](#block-processing)
|
- [Block processing](#block-processing)
|
||||||
- [Modified `process_attestation`](#modified-process_attestation)
|
- [Modified `process_attestation`](#modified-process_attestation)
|
||||||
- [New `process_deposit`](#new-process_deposit)
|
- [Modified `process_deposit`](#modified-process_deposit)
|
||||||
- [Sync committee processing](#sync-committee-processing)
|
- [Sync committee processing](#sync-committee-processing)
|
||||||
- [Epoch processing](#epoch-processing)
|
- [Epoch processing](#epoch-processing)
|
||||||
- [Justification and finalization](#justification-and-finalization)
|
- [Justification and finalization](#justification-and-finalization)
|
||||||
|
- [Leak scores](#leak-scores)
|
||||||
- [Rewards and penalties](#rewards-and-penalties)
|
- [Rewards and penalties](#rewards-and-penalties)
|
||||||
- [Slashings](#slashings)
|
- [Slashings](#slashings)
|
||||||
- [Participation flags updates](#participation-flags-updates)
|
- [Participation flags updates](#participation-flags-updates)
|
||||||
@ -118,6 +119,7 @@ This patch updates a few configuration values to move penalty constants toward t
|
|||||||
| - | - |
|
| - | - |
|
||||||
| `SYNC_COMMITTEE_SIZE` | `uint64(2**10)` (= 1,024) |
|
| `SYNC_COMMITTEE_SIZE` | `uint64(2**10)` (= 1,024) |
|
||||||
| `SYNC_SUBCOMMITTEE_SIZE` | `uint64(2**6)` (= 64) |
|
| `SYNC_SUBCOMMITTEE_SIZE` | `uint64(2**6)` (= 64) |
|
||||||
|
| `LEAK_SCORE_BIAS` | 4 |
|
||||||
|
|
||||||
### Time parameters
|
### Time parameters
|
||||||
|
|
||||||
@ -133,10 +135,7 @@ This patch updates a few configuration values to move penalty constants toward t
|
|||||||
|
|
||||||
## Containers
|
## Containers
|
||||||
|
|
||||||
### Extended containers
|
### Modified containers
|
||||||
|
|
||||||
*Note*: Extended SSZ containers inherit all fields from the parent in the original
|
|
||||||
order and append any additional fields to the end.
|
|
||||||
|
|
||||||
#### `BeaconBlockBody`
|
#### `BeaconBlockBody`
|
||||||
|
|
||||||
@ -182,16 +181,18 @@ class BeaconState(Container):
|
|||||||
# Slashings
|
# Slashings
|
||||||
slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] # Per-epoch sums of slashed effective balances
|
slashings: Vector[Gwei, EPOCHS_PER_SLASHINGS_VECTOR] # Per-epoch sums of slashed effective balances
|
||||||
# Participation
|
# Participation
|
||||||
previous_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT]
|
previous_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] # [New in Altair]
|
||||||
current_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT]
|
current_epoch_participation: List[ParticipationFlags, VALIDATOR_REGISTRY_LIMIT] # [New in Altair]
|
||||||
# Finality
|
# Finality
|
||||||
justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] # Bit set for every recent justified epoch
|
justification_bits: Bitvector[JUSTIFICATION_BITS_LENGTH] # Bit set for every recent justified epoch
|
||||||
previous_justified_checkpoint: Checkpoint
|
previous_justified_checkpoint: Checkpoint
|
||||||
current_justified_checkpoint: Checkpoint
|
current_justified_checkpoint: Checkpoint
|
||||||
finalized_checkpoint: Checkpoint
|
finalized_checkpoint: Checkpoint
|
||||||
# Light client sync committees
|
# Light client sync committees
|
||||||
current_sync_committee: SyncCommittee
|
current_sync_committee: SyncCommittee # [New in Altair]
|
||||||
next_sync_committee: SyncCommittee
|
next_sync_committee: SyncCommittee # [New in Altair]
|
||||||
|
# Leak
|
||||||
|
leak_scores: List[uint64, VALIDATOR_REGISTRY_LIMIT] # [New in Altair]
|
||||||
```
|
```
|
||||||
|
|
||||||
### New containers
|
### New containers
|
||||||
@ -284,13 +285,10 @@ def get_sync_committee(state: BeaconState, epoch: Epoch) -> SyncCommittee:
|
|||||||
Return the sync committee for a given state and epoch.
|
Return the sync committee for a given state and epoch.
|
||||||
"""
|
"""
|
||||||
indices = get_sync_committee_indices(state, epoch)
|
indices = get_sync_committee_indices(state, epoch)
|
||||||
validators = [state.validators[index] for index in indices]
|
pubkeys = [state.validators[index].pubkey for index in indices]
|
||||||
pubkeys = [validator.pubkey for validator in validators]
|
subcommitees = [pubkeys[i:i + SYNC_SUBCOMMITTEE_SIZE] for i in range(0, len(pubkeys), SYNC_SUBCOMMITTEE_SIZE)]
|
||||||
aggregates = [
|
pubkey_aggregates = [bls.AggregatePKs(subcommitee) for subcommitee in subcommitees]
|
||||||
bls.AggregatePKs(pubkeys[i:i + SYNC_SUBCOMMITTEE_SIZE])
|
return SyncCommittee(pubkeys=pubkeys, pubkey_aggregates=pubkey_aggregates)
|
||||||
for i in range(0, len(pubkeys), SYNC_SUBCOMMITTEE_SIZE)
|
|
||||||
]
|
|
||||||
return SyncCommittee(pubkeys=pubkeys, pubkey_aggregates=aggregates)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `get_base_reward`
|
#### `get_base_reward`
|
||||||
@ -366,8 +364,12 @@ def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], S
|
|||||||
flags to determine who participated and who did not, applying the leak penalty globally and applying
|
flags to determine who participated and who did not, applying the leak penalty globally and applying
|
||||||
compensatory rewards to participants.
|
compensatory rewards to participants.
|
||||||
"""
|
"""
|
||||||
|
rewards = [Gwei(0) for _ in range(len(state.validators))]
|
||||||
penalties = [Gwei(0) for _ in range(len(state.validators))]
|
penalties = [Gwei(0) for _ in range(len(state.validators))]
|
||||||
if is_in_inactivity_leak(state):
|
|
||||||
|
if not is_in_inactivity_leak(state):
|
||||||
|
return rewards, penalties
|
||||||
|
|
||||||
reward_numerator_sum = sum(numerator for (_, numerator) in get_flag_indices_and_numerators())
|
reward_numerator_sum = sum(numerator for (_, numerator) in get_flag_indices_and_numerators())
|
||||||
matching_target_attesting_indices = get_unslashed_participating_indices(
|
matching_target_attesting_indices = get_unslashed_participating_indices(
|
||||||
state, TIMELY_TARGET_FLAG_INDEX, get_previous_epoch(state)
|
state, TIMELY_TARGET_FLAG_INDEX, get_previous_epoch(state)
|
||||||
@ -375,14 +377,13 @@ def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], S
|
|||||||
for index in get_eligible_validator_indices(state):
|
for index in get_eligible_validator_indices(state):
|
||||||
# If validator is performing optimally this cancels all attestation rewards for a neutral balance
|
# If validator is performing optimally this cancels all attestation rewards for a neutral balance
|
||||||
penalties[index] += Gwei(get_base_reward(state, index) * reward_numerator_sum // FLAG_DENOMINATOR)
|
penalties[index] += Gwei(get_base_reward(state, index) * reward_numerator_sum // FLAG_DENOMINATOR)
|
||||||
if index not in matching_target_attesting_indices:
|
if index not in matching_target_attesting_indices and state.leak_scores[index] >= LEAK_SCORE_BIAS:
|
||||||
effective_balance = state.validators[index].effective_balance
|
effective_balance = state.validators[index].effective_balance
|
||||||
penalties[index] += Gwei(
|
leak_penalty = Gwei(
|
||||||
effective_balance * get_finality_delay(state)
|
effective_balance * state.leak_scores[index] // LEAK_SCORE_BIAS // ALTAIR_INACTIVITY_PENALTY_QUOTIENT
|
||||||
// ALTAIR_INACTIVITY_PENALTY_QUOTIENT
|
|
||||||
)
|
)
|
||||||
|
penalties[index] += leak_penalty
|
||||||
|
|
||||||
rewards = [Gwei(0) for _ in range(len(state.validators))]
|
|
||||||
return rewards, penalties
|
return rewards, penalties
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -483,9 +484,9 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None:
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
#### New `process_deposit`
|
#### Modified `process_deposit`
|
||||||
|
|
||||||
*Note*: The function `process_deposit` is modified to initialize `previous_epoch_participation` and `current_epoch_participation`.
|
*Note*: The function `process_deposit` is modified to initialize `leak_scores`, `previous_epoch_participation`, `current_epoch_participation`.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
||||||
@ -521,6 +522,7 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None:
|
|||||||
state.balances.append(amount)
|
state.balances.append(amount)
|
||||||
state.previous_epoch_participation.append(ParticipationFlags(0b0000_0000)) # [New in Altair]
|
state.previous_epoch_participation.append(ParticipationFlags(0b0000_0000)) # [New in Altair]
|
||||||
state.current_epoch_participation.append(ParticipationFlags(0b0000_0000)) # [New in Altair]
|
state.current_epoch_participation.append(ParticipationFlags(0b0000_0000)) # [New in Altair]
|
||||||
|
state.leak_scores.append(0) # [New in Altair]
|
||||||
else:
|
else:
|
||||||
# Increase balance by deposit amount
|
# Increase balance by deposit amount
|
||||||
index = ValidatorIndex(validator_pubkeys.index(pubkey))
|
index = ValidatorIndex(validator_pubkeys.index(pubkey))
|
||||||
@ -561,6 +563,7 @@ def process_sync_committee(state: BeaconState, body: BeaconBlockBody) -> None:
|
|||||||
```python
|
```python
|
||||||
def process_epoch(state: BeaconState) -> None:
|
def process_epoch(state: BeaconState) -> None:
|
||||||
process_justification_and_finalization(state) # [Modified in Altair]
|
process_justification_and_finalization(state) # [Modified in Altair]
|
||||||
|
process_leak_updates(state) # [New in Altair]
|
||||||
process_rewards_and_penalties(state) # [Modified in Altair]
|
process_rewards_and_penalties(state) # [Modified in Altair]
|
||||||
process_registry_updates(state)
|
process_registry_updates(state)
|
||||||
process_slashings(state) # [Modified in Altair]
|
process_slashings(state) # [Modified in Altair]
|
||||||
@ -619,6 +622,23 @@ def process_justification_and_finalization(state: BeaconState) -> None:
|
|||||||
state.finalized_checkpoint = old_current_justified_checkpoint
|
state.finalized_checkpoint = old_current_justified_checkpoint
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Leak scores
|
||||||
|
|
||||||
|
*Note*: The function `process_leak_updates` is new.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def process_leak_updates(state: BeaconState) -> None:
|
||||||
|
matching_target_attesting_indices = get_unslashed_participating_indices(
|
||||||
|
state, TIMELY_TARGET_FLAG_INDEX, get_previous_epoch(state)
|
||||||
|
)
|
||||||
|
for index in get_eligible_validator_indices(state):
|
||||||
|
if index in matching_target_attesting_indices:
|
||||||
|
if state.leak_scores[index] > 0:
|
||||||
|
state.leak_scores[index] -= 1
|
||||||
|
elif is_in_inactivity_leak(state):
|
||||||
|
state.leak_scores[index] += LEAK_SCORE_BIAS
|
||||||
|
```
|
||||||
|
|
||||||
#### Rewards and penalties
|
#### Rewards and penalties
|
||||||
|
|
||||||
*Note*: The function `process_rewards_and_penalties` is modified to support the incentive reforms.
|
*Note*: The function `process_rewards_and_penalties` is modified to support the incentive reforms.
|
||||||
|
@ -42,6 +42,7 @@ After `process_slots` of Phase 0 finishes, if `state.slot == ALTAIR_FORK_SLOT`,
|
|||||||
def upgrade_to_altair(pre: phase0.BeaconState) -> BeaconState:
|
def upgrade_to_altair(pre: phase0.BeaconState) -> BeaconState:
|
||||||
epoch = get_current_epoch(pre)
|
epoch = get_current_epoch(pre)
|
||||||
post = BeaconState(
|
post = BeaconState(
|
||||||
|
# Versioning
|
||||||
genesis_time=pre.genesis_time,
|
genesis_time=pre.genesis_time,
|
||||||
genesis_validators_root=pre.genesis_validators_root,
|
genesis_validators_root=pre.genesis_validators_root,
|
||||||
slot=pre.slot,
|
slot=pre.slot,
|
||||||
@ -67,13 +68,15 @@ def upgrade_to_altair(pre: phase0.BeaconState) -> BeaconState:
|
|||||||
# Slashings
|
# Slashings
|
||||||
slashings=pre.slashings,
|
slashings=pre.slashings,
|
||||||
# Participation
|
# Participation
|
||||||
previous_epoch_participation=[ParticipationFlags(0) for _ in range(len(pre.validators))],
|
previous_epoch_participation=[ParticipationFlags(0b0000_0000) for _ in range(len(pre.validators))],
|
||||||
current_epoch_participation=[ParticipationFlags(0) for _ in range(len(pre.validators))],
|
current_epoch_participation=[ParticipationFlags(0b0000_0000) for _ in range(len(pre.validators))],
|
||||||
# Finality
|
# Finality
|
||||||
justification_bits=pre.justification_bits,
|
justification_bits=pre.justification_bits,
|
||||||
previous_justified_checkpoint=pre.previous_justified_checkpoint,
|
previous_justified_checkpoint=pre.previous_justified_checkpoint,
|
||||||
current_justified_checkpoint=pre.current_justified_checkpoint,
|
current_justified_checkpoint=pre.current_justified_checkpoint,
|
||||||
finalized_checkpoint=pre.finalized_checkpoint,
|
finalized_checkpoint=pre.finalized_checkpoint,
|
||||||
|
# Leak
|
||||||
|
leak_scores=[0 for _ in range(len(pre.validators))],
|
||||||
)
|
)
|
||||||
# Fill in sync committees
|
# Fill in sync committees
|
||||||
post.current_sync_committee = get_sync_committee(post, get_current_epoch(post))
|
post.current_sync_committee = get_sync_committee(post, get_current_epoch(post))
|
||||||
|
@ -2,9 +2,11 @@ import random
|
|||||||
from eth2spec.test.helpers.state import (
|
from eth2spec.test.helpers.state import (
|
||||||
state_transition_and_sign_block,
|
state_transition_and_sign_block,
|
||||||
next_epoch,
|
next_epoch,
|
||||||
|
next_epoch_via_block,
|
||||||
)
|
)
|
||||||
from eth2spec.test.helpers.block import (
|
from eth2spec.test.helpers.block import (
|
||||||
build_empty_block_for_next_slot,
|
build_empty_block_for_next_slot,
|
||||||
|
build_empty_block,
|
||||||
)
|
)
|
||||||
from eth2spec.test.helpers.sync_committee import (
|
from eth2spec.test.helpers.sync_committee import (
|
||||||
compute_aggregate_sync_committee_signature,
|
compute_aggregate_sync_committee_signature,
|
||||||
@ -73,3 +75,25 @@ def test_half_sync_committee_committee_genesis(spec, state):
|
|||||||
@spec_state_test
|
@spec_state_test
|
||||||
def test_empty_sync_committee_committee_genesis(spec, state):
|
def test_empty_sync_committee_committee_genesis(spec, state):
|
||||||
yield from run_sync_committee_sanity_test(spec, state, fraction_full=0.0)
|
yield from run_sync_committee_sanity_test(spec, state, fraction_full=0.0)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases_except([PHASE0, PHASE1])
|
||||||
|
@spec_state_test
|
||||||
|
def test_leak_scores(spec, state):
|
||||||
|
for _ in range(spec.MIN_EPOCHS_TO_INACTIVITY_PENALTY + 2):
|
||||||
|
next_epoch_via_block(spec, state)
|
||||||
|
|
||||||
|
assert spec.is_in_inactivity_leak(state)
|
||||||
|
previous_leak_scores = state.leak_scores.copy()
|
||||||
|
|
||||||
|
yield 'pre', state
|
||||||
|
|
||||||
|
# Block transition to next epoch
|
||||||
|
block = build_empty_block(spec, state, slot=state.slot + spec.SLOTS_PER_EPOCH)
|
||||||
|
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||||
|
|
||||||
|
yield 'blocks', [signed_block]
|
||||||
|
yield 'post', state
|
||||||
|
|
||||||
|
for pre, post in zip(previous_leak_scores, state.leak_scores):
|
||||||
|
assert post == pre + spec.LEAK_SCORE_BIAS
|
||||||
|
@ -34,6 +34,7 @@ def run_deltas(spec, state):
|
|||||||
- source deltas ('source_deltas')
|
- source deltas ('source_deltas')
|
||||||
- target deltas ('target_deltas')
|
- target deltas ('target_deltas')
|
||||||
- head deltas ('head_deltas')
|
- head deltas ('head_deltas')
|
||||||
|
- not if is_post_altair(spec)
|
||||||
- inclusion delay deltas ('inclusion_delay_deltas')
|
- inclusion delay deltas ('inclusion_delay_deltas')
|
||||||
- inactivity penalty deltas ('inactivity_penalty_deltas')
|
- inactivity penalty deltas ('inactivity_penalty_deltas')
|
||||||
"""
|
"""
|
||||||
@ -70,6 +71,7 @@ def run_deltas(spec, state):
|
|||||||
spec.get_matching_head_attestations,
|
spec.get_matching_head_attestations,
|
||||||
'head_deltas',
|
'head_deltas',
|
||||||
)
|
)
|
||||||
|
if not is_post_altair(spec):
|
||||||
yield from run_get_inclusion_delay_deltas(spec, state)
|
yield from run_get_inclusion_delay_deltas(spec, state)
|
||||||
yield from run_get_inactivity_penalty_deltas(spec, state)
|
yield from run_get_inactivity_penalty_deltas(spec, state)
|
||||||
|
|
||||||
@ -219,7 +221,8 @@ def run_get_inactivity_penalty_deltas(spec, state):
|
|||||||
|
|
||||||
def transition_state_to_leak(spec, state, epochs=None):
|
def transition_state_to_leak(spec, state, epochs=None):
|
||||||
if epochs is None:
|
if epochs is None:
|
||||||
epochs = spec.MIN_EPOCHS_TO_INACTIVITY_PENALTY
|
# +1 to trigger leak_score transitions
|
||||||
|
epochs = spec.MIN_EPOCHS_TO_INACTIVITY_PENALTY + 1
|
||||||
assert epochs >= spec.MIN_EPOCHS_TO_INACTIVITY_PENALTY
|
assert epochs >= spec.MIN_EPOCHS_TO_INACTIVITY_PENALTY
|
||||||
|
|
||||||
for _ in range(epochs):
|
for _ in range(epochs):
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
|
import random
|
||||||
|
|
||||||
from eth2spec.test.context import (
|
from eth2spec.test.context import (
|
||||||
spec_state_test, expect_assertion_error, always_bls, with_all_phases
|
spec_state_test, expect_assertion_error, always_bls, with_all_phases,
|
||||||
|
with_custom_state, spec_test, single_phase,
|
||||||
|
low_balances, misc_balances,
|
||||||
)
|
)
|
||||||
from eth2spec.test.helpers.attestations import sign_indexed_attestation
|
from eth2spec.test.helpers.attestations import sign_indexed_attestation
|
||||||
from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing, \
|
from eth2spec.test.helpers.attester_slashings import get_valid_attester_slashing, \
|
||||||
@ -32,15 +36,19 @@ def run_attester_slashing_processing(spec, state, attester_slashing, valid=True)
|
|||||||
|
|
||||||
proposer_index = spec.get_beacon_proposer_index(state)
|
proposer_index = spec.get_beacon_proposer_index(state)
|
||||||
pre_proposer_balance = get_balance(state, proposer_index)
|
pre_proposer_balance = get_balance(state, proposer_index)
|
||||||
pre_slashings = {slashed_index: get_balance(state, slashed_index) for slashed_index in slashed_indices}
|
pre_slashing_balances = {slashed_index: get_balance(state, slashed_index) for slashed_index in slashed_indices}
|
||||||
|
pre_slashing_effectives = {
|
||||||
|
slashed_index: state.validators[slashed_index].effective_balance
|
||||||
|
for slashed_index in slashed_indices
|
||||||
|
}
|
||||||
pre_withdrawalable_epochs = {
|
pre_withdrawalable_epochs = {
|
||||||
slashed_index: state.validators[slashed_index].withdrawable_epoch
|
slashed_index: state.validators[slashed_index].withdrawable_epoch
|
||||||
for slashed_index in slashed_indices
|
for slashed_index in slashed_indices
|
||||||
}
|
}
|
||||||
|
|
||||||
total_proposer_rewards = sum(
|
total_proposer_rewards = sum(
|
||||||
balance // spec.WHISTLEBLOWER_REWARD_QUOTIENT
|
effective_balance // spec.WHISTLEBLOWER_REWARD_QUOTIENT
|
||||||
for balance in pre_slashings.values()
|
for effective_balance in pre_slashing_effectives.values()
|
||||||
)
|
)
|
||||||
|
|
||||||
# Process slashing
|
# Process slashing
|
||||||
@ -61,7 +69,7 @@ def run_attester_slashing_processing(spec, state, attester_slashing, valid=True)
|
|||||||
assert slashed_validator.withdrawable_epoch == expected_withdrawable_epoch
|
assert slashed_validator.withdrawable_epoch == expected_withdrawable_epoch
|
||||||
else:
|
else:
|
||||||
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
|
assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH
|
||||||
assert get_balance(state, slashed_index) < pre_slashings[slashed_index]
|
assert get_balance(state, slashed_index) < pre_slashing_balances[slashed_index]
|
||||||
|
|
||||||
if proposer_index not in slashed_indices:
|
if proposer_index not in slashed_indices:
|
||||||
# gained whistleblower reward
|
# gained whistleblower reward
|
||||||
@ -71,7 +79,7 @@ def run_attester_slashing_processing(spec, state, attester_slashing, valid=True)
|
|||||||
expected_balance = (
|
expected_balance = (
|
||||||
pre_proposer_balance
|
pre_proposer_balance
|
||||||
+ total_proposer_rewards
|
+ total_proposer_rewards
|
||||||
- pre_slashings[proposer_index] // get_min_slashing_penalty_quotient(spec)
|
- pre_slashing_effectives[proposer_index] // get_min_slashing_penalty_quotient(spec)
|
||||||
)
|
)
|
||||||
|
|
||||||
assert get_balance(state, proposer_index) == expected_balance
|
assert get_balance(state, proposer_index) == expected_balance
|
||||||
@ -118,6 +126,41 @@ def test_success_already_exited_recent(spec, state):
|
|||||||
yield from run_attester_slashing_processing(spec, state, attester_slashing)
|
yield from run_attester_slashing_processing(spec, state, attester_slashing)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
|
||||||
|
@spec_test
|
||||||
|
@single_phase
|
||||||
|
def test_success_low_balances(spec, state):
|
||||||
|
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True)
|
||||||
|
|
||||||
|
yield from run_attester_slashing_processing(spec, state, attester_slashing)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
|
||||||
|
@spec_test
|
||||||
|
@single_phase
|
||||||
|
def test_success_misc_balances(spec, state):
|
||||||
|
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True)
|
||||||
|
|
||||||
|
yield from run_attester_slashing_processing(spec, state, attester_slashing)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
|
||||||
|
@spec_test
|
||||||
|
@single_phase
|
||||||
|
def test_success_with_effective_balance_disparity(spec, state):
|
||||||
|
# Jitter balances to be different from effective balances
|
||||||
|
for i in range(len(state.balances)):
|
||||||
|
pre = int(state.balances[i])
|
||||||
|
state.balances[i] += random.randrange(max(pre - 5000, 0), pre + 5000)
|
||||||
|
|
||||||
|
attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True)
|
||||||
|
|
||||||
|
yield from run_attester_slashing_processing(spec, state, attester_slashing)
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases
|
@with_all_phases
|
||||||
@spec_state_test
|
@spec_state_test
|
||||||
@always_bls
|
@always_bls
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
from eth2spec.test.context import spec_state_test, with_all_phases
|
from eth2spec.test.context import spec_state_test, with_all_phases
|
||||||
from eth2spec.test.helpers.epoch_processing import (
|
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_to
|
||||||
run_epoch_processing_with, run_epoch_processing_to
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def run_process_effective_balance_updates(spec, state):
|
|
||||||
yield from run_epoch_processing_with(spec, state, 'process_effective_balance_updates')
|
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases
|
@with_all_phases
|
||||||
@ -44,7 +38,9 @@ def test_effective_balance_hysteresis(spec, state):
|
|||||||
state.validators[i].effective_balance = pre_eff
|
state.validators[i].effective_balance = pre_eff
|
||||||
state.balances[i] = bal
|
state.balances[i] = bal
|
||||||
|
|
||||||
yield from run_process_effective_balance_updates(spec, state)
|
yield 'pre', state
|
||||||
|
spec.process_effective_balance_updates(state)
|
||||||
|
yield 'post', state
|
||||||
|
|
||||||
for i, (_, _, post_eff, name) in enumerate(cases):
|
for i, (_, _, post_eff, name) in enumerate(cases):
|
||||||
assert state.validators[i].effective_balance == post_eff, name
|
assert state.validators[i].effective_balance == post_eff, name
|
||||||
|
@ -27,6 +27,43 @@ def run_process_rewards_and_penalties(spec, state):
|
|||||||
yield from run_epoch_processing_with(spec, state, 'process_rewards_and_penalties')
|
yield from run_epoch_processing_with(spec, state, 'process_rewards_and_penalties')
|
||||||
|
|
||||||
|
|
||||||
|
def validate_resulting_balances(spec, pre_state, post_state, attestations):
|
||||||
|
attesting_indices = spec.get_unslashed_attesting_indices(post_state, attestations)
|
||||||
|
current_epoch = spec.get_current_epoch(post_state)
|
||||||
|
|
||||||
|
for index in range(len(pre_state.validators)):
|
||||||
|
if not spec.is_active_validator(pre_state.validators[index], current_epoch):
|
||||||
|
assert post_state.balances[index] == pre_state.balances[index]
|
||||||
|
elif not is_post_altair(spec):
|
||||||
|
proposer_indices = [a.proposer_index for a in post_state.previous_epoch_attestations]
|
||||||
|
if spec.is_in_inactivity_leak(post_state):
|
||||||
|
# Proposers can still make money during a leak before LIGHTCLIENT_PATCH
|
||||||
|
if index in proposer_indices and index in attesting_indices:
|
||||||
|
assert post_state.balances[index] > pre_state.balances[index]
|
||||||
|
elif index in attesting_indices:
|
||||||
|
# If not proposer but participated optimally, should have exactly neutral balance
|
||||||
|
assert post_state.balances[index] == pre_state.balances[index]
|
||||||
|
else:
|
||||||
|
assert post_state.balances[index] < pre_state.balances[index]
|
||||||
|
else:
|
||||||
|
if index in attesting_indices:
|
||||||
|
assert post_state.balances[index] > pre_state.balances[index]
|
||||||
|
else:
|
||||||
|
assert post_state.balances[index] < pre_state.balances[index]
|
||||||
|
else:
|
||||||
|
if spec.is_in_inactivity_leak(post_state):
|
||||||
|
if index in attesting_indices:
|
||||||
|
# If not proposer but participated optimally, should have exactly neutral balance
|
||||||
|
assert post_state.balances[index] == pre_state.balances[index]
|
||||||
|
else:
|
||||||
|
assert post_state.balances[index] < pre_state.balances[index]
|
||||||
|
else:
|
||||||
|
if index in attesting_indices:
|
||||||
|
assert post_state.balances[index] > pre_state.balances[index]
|
||||||
|
else:
|
||||||
|
assert post_state.balances[index] < pre_state.balances[index]
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases
|
@with_all_phases
|
||||||
@spec_state_test
|
@spec_state_test
|
||||||
def test_genesis_epoch_no_attestations_no_penalties(spec, state):
|
def test_genesis_epoch_no_attestations_no_penalties(spec, state):
|
||||||
@ -100,19 +137,10 @@ def test_full_attestations_misc_balances(spec, state):
|
|||||||
|
|
||||||
yield from run_process_rewards_and_penalties(spec, state)
|
yield from run_process_rewards_and_penalties(spec, state)
|
||||||
|
|
||||||
attesting_indices = spec.get_unslashed_attesting_indices(state, attestations)
|
validate_resulting_balances(spec, pre_state, state, attestations)
|
||||||
assert len(attesting_indices) > 0
|
|
||||||
assert len(attesting_indices) != len(pre_state.validators)
|
|
||||||
assert any(v.effective_balance != spec.MAX_EFFECTIVE_BALANCE for v in state.validators)
|
|
||||||
for index in range(len(pre_state.validators)):
|
|
||||||
if index in attesting_indices:
|
|
||||||
assert state.balances[index] > pre_state.balances[index]
|
|
||||||
elif spec.is_active_validator(pre_state.validators[index], spec.compute_epoch_at_slot(state.slot)):
|
|
||||||
assert state.balances[index] < pre_state.balances[index]
|
|
||||||
else:
|
|
||||||
assert state.balances[index] == pre_state.balances[index]
|
|
||||||
# Check if base rewards are consistent with effective balance.
|
# Check if base rewards are consistent with effective balance.
|
||||||
brs = {}
|
brs = {}
|
||||||
|
attesting_indices = spec.get_unslashed_attesting_indices(state, attestations)
|
||||||
for index in attesting_indices:
|
for index in attesting_indices:
|
||||||
br = spec.get_base_reward(state, index)
|
br = spec.get_base_reward(state, index)
|
||||||
if br in brs:
|
if br in brs:
|
||||||
@ -146,8 +174,7 @@ def test_no_attestations_all_penalties(spec, state):
|
|||||||
|
|
||||||
yield from run_process_rewards_and_penalties(spec, state)
|
yield from run_process_rewards_and_penalties(spec, state)
|
||||||
|
|
||||||
for index in range(len(pre_state.validators)):
|
validate_resulting_balances(spec, pre_state, state, [])
|
||||||
assert state.balances[index] < pre_state.balances[index]
|
|
||||||
|
|
||||||
|
|
||||||
def run_with_participation(spec, state, participation_fn):
|
def run_with_participation(spec, state, participation_fn):
|
||||||
@ -161,35 +188,12 @@ def run_with_participation(spec, state, participation_fn):
|
|||||||
attestations = prepare_state_with_attestations(spec, state, participation_fn=participation_tracker)
|
attestations = prepare_state_with_attestations(spec, state, participation_fn=participation_tracker)
|
||||||
pre_state = state.copy()
|
pre_state = state.copy()
|
||||||
|
|
||||||
if not is_post_altair(spec):
|
|
||||||
proposer_indices = [a.proposer_index for a in state.previous_epoch_attestations]
|
|
||||||
else:
|
|
||||||
sync_committee_indices = spec.get_sync_committee_indices(state, spec.get_current_epoch(state))
|
|
||||||
|
|
||||||
yield from run_process_rewards_and_penalties(spec, state)
|
yield from run_process_rewards_and_penalties(spec, state)
|
||||||
|
|
||||||
attesting_indices = spec.get_unslashed_attesting_indices(state, attestations)
|
attesting_indices = spec.get_unslashed_attesting_indices(state, attestations)
|
||||||
assert len(attesting_indices) == len(participated)
|
assert len(attesting_indices) == len(participated)
|
||||||
|
|
||||||
for index in range(len(pre_state.validators)):
|
validate_resulting_balances(spec, pre_state, state, attestations)
|
||||||
if spec.is_in_inactivity_leak(state):
|
|
||||||
# Proposers can still make money during a leak before ALTAIR
|
|
||||||
if not is_post_altair(spec) and index in proposer_indices and index in participated:
|
|
||||||
assert state.balances[index] > pre_state.balances[index]
|
|
||||||
elif index in attesting_indices:
|
|
||||||
if is_post_altair(spec) and index in sync_committee_indices:
|
|
||||||
# The sync committee reward has not been canceled, so the sync committee participants still earn it
|
|
||||||
assert state.balances[index] >= pre_state.balances[index]
|
|
||||||
else:
|
|
||||||
# If not proposer but participated optimally, should have exactly neutral balance
|
|
||||||
assert state.balances[index] == pre_state.balances[index]
|
|
||||||
else:
|
|
||||||
assert state.balances[index] < pre_state.balances[index]
|
|
||||||
else:
|
|
||||||
if index in participated:
|
|
||||||
assert state.balances[index] > pre_state.balances[index]
|
|
||||||
else:
|
|
||||||
assert state.balances[index] < pre_state.balances[index]
|
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases
|
@with_all_phases
|
||||||
@ -438,10 +442,4 @@ def test_attestations_some_slashed(spec, state):
|
|||||||
attesting_indices = spec.get_unslashed_attesting_indices(state, attestations)
|
attesting_indices = spec.get_unslashed_attesting_indices(state, attestations)
|
||||||
assert len(attesting_indices) > 0
|
assert len(attesting_indices) > 0
|
||||||
assert len(attesting_indices_before_slashings) - len(attesting_indices) == spec.MIN_PER_EPOCH_CHURN_LIMIT
|
assert len(attesting_indices_before_slashings) - len(attesting_indices) == spec.MIN_PER_EPOCH_CHURN_LIMIT
|
||||||
for index in range(len(pre_state.validators)):
|
validate_resulting_balances(spec, pre_state, state, attestations)
|
||||||
if index in attesting_indices:
|
|
||||||
# non-slashed attester should gain reward
|
|
||||||
assert state.balances[index] > pre_state.balances[index]
|
|
||||||
else:
|
|
||||||
# Slashed non-proposer attester should have penalty
|
|
||||||
assert state.balances[index] < pre_state.balances[index]
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user