Merge pull request #1747 from ethereum/rewards-rework
[Proposal] rework rewards/penalties to be more granular
This commit is contained in:
commit
6474218fb1
|
@ -110,6 +110,10 @@
|
||||||
- [Helper functions](#helper-functions-1)
|
- [Helper functions](#helper-functions-1)
|
||||||
- [Justification and finalization](#justification-and-finalization)
|
- [Justification and finalization](#justification-and-finalization)
|
||||||
- [Rewards and penalties](#rewards-and-penalties-1)
|
- [Rewards and penalties](#rewards-and-penalties-1)
|
||||||
|
- [Helpers](#helpers)
|
||||||
|
- [Components of attestation deltas](#components-of-attestation-deltas)
|
||||||
|
- [`get_attestation_deltas`](#get_attestation_deltas)
|
||||||
|
- [`process_rewards_and_penalties`](#process_rewards_and_penalties)
|
||||||
- [Registry updates](#registry-updates)
|
- [Registry updates](#registry-updates)
|
||||||
- [Slashings](#slashings)
|
- [Slashings](#slashings)
|
||||||
- [Final updates](#final-updates)
|
- [Final updates](#final-updates)
|
||||||
|
@ -1341,6 +1345,8 @@ def process_justification_and_finalization(state: BeaconState) -> None:
|
||||||
|
|
||||||
#### Rewards and penalties
|
#### Rewards and penalties
|
||||||
|
|
||||||
|
##### Helpers
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
|
def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
|
||||||
total_balance = get_total_active_balance(state)
|
total_balance = get_total_active_balance(state)
|
||||||
|
@ -1349,32 +1355,72 @@ def get_base_reward(state: BeaconState, index: ValidatorIndex) -> Gwei:
|
||||||
```
|
```
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def get_attestation_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
def get_eligible_validator_indices(state: BeaconState) -> Sequence[ValidatorIndex]:
|
||||||
previous_epoch = get_previous_epoch(state)
|
previous_epoch = get_previous_epoch(state)
|
||||||
total_balance = get_total_active_balance(state)
|
return [
|
||||||
rewards = [Gwei(0) for _ in range(len(state.validators))]
|
|
||||||
penalties = [Gwei(0) for _ in range(len(state.validators))]
|
|
||||||
eligible_validator_indices = [
|
|
||||||
ValidatorIndex(index) for index, v in enumerate(state.validators)
|
ValidatorIndex(index) for index, v in enumerate(state.validators)
|
||||||
if is_active_validator(v, previous_epoch) or (v.slashed and previous_epoch + 1 < v.withdrawable_epoch)
|
if is_active_validator(v, previous_epoch) or (v.slashed and previous_epoch + 1 < v.withdrawable_epoch)
|
||||||
]
|
]
|
||||||
|
```
|
||||||
|
|
||||||
# Micro-incentives for matching FFG source, FFG target, and head
|
```python
|
||||||
matching_source_attestations = get_matching_source_attestations(state, previous_epoch)
|
def get_attestation_component_deltas(state: BeaconState,
|
||||||
matching_target_attestations = get_matching_target_attestations(state, previous_epoch)
|
attestations: Sequence[PendingAttestation]
|
||||||
matching_head_attestations = get_matching_head_attestations(state, previous_epoch)
|
) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
||||||
for attestations in (matching_source_attestations, matching_target_attestations, matching_head_attestations):
|
"""
|
||||||
unslashed_attesting_indices = get_unslashed_attesting_indices(state, attestations)
|
Helper with shared logic for use by get source, target, and head deltas functions
|
||||||
attesting_balance = get_total_balance(state, unslashed_attesting_indices)
|
"""
|
||||||
for index in eligible_validator_indices:
|
rewards = [Gwei(0)] * len(state.validators)
|
||||||
if index in unslashed_attesting_indices:
|
penalties = [Gwei(0)] * len(state.validators)
|
||||||
increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from balance totals to avoid uint64 overflow
|
total_balance = get_total_active_balance(state)
|
||||||
reward_numerator = get_base_reward(state, index) * (attesting_balance // increment)
|
unslashed_attesting_indices = get_unslashed_attesting_indices(state, attestations)
|
||||||
rewards[index] += reward_numerator // (total_balance // increment)
|
attesting_balance = get_total_balance(state, unslashed_attesting_indices)
|
||||||
else:
|
for index in get_eligible_validator_indices(state):
|
||||||
penalties[index] += get_base_reward(state, index)
|
if index in unslashed_attesting_indices:
|
||||||
|
increment = EFFECTIVE_BALANCE_INCREMENT # Factored out from balance totals to avoid uint64 overflow
|
||||||
|
reward_numerator = get_base_reward(state, index) * (attesting_balance // increment)
|
||||||
|
rewards[index] += reward_numerator // (total_balance // increment)
|
||||||
|
else:
|
||||||
|
penalties[index] += get_base_reward(state, index)
|
||||||
|
return rewards, penalties
|
||||||
|
```
|
||||||
|
|
||||||
# Proposer and inclusion delay micro-rewards
|
##### Components of attestation deltas
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_source_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
||||||
|
"""
|
||||||
|
Return attester micro-rewards/penalties for source-vote for each validator.
|
||||||
|
"""
|
||||||
|
matching_source_attestations = get_matching_source_attestations(state, get_previous_epoch(state))
|
||||||
|
return get_attestation_component_deltas(state, matching_source_attestations)
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_target_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
||||||
|
"""
|
||||||
|
Return attester micro-rewards/penalties for target-vote for each validator.
|
||||||
|
"""
|
||||||
|
matching_target_attestations = get_matching_target_attestations(state, get_previous_epoch(state))
|
||||||
|
return get_attestation_component_deltas(state, matching_target_attestations)
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_head_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
||||||
|
"""
|
||||||
|
Return attester micro-rewards/penalties for head-vote for each validator.
|
||||||
|
"""
|
||||||
|
matching_head_attestations = get_matching_head_attestations(state, get_previous_epoch(state))
|
||||||
|
return get_attestation_component_deltas(state, matching_head_attestations)
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_inclusion_delay_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
||||||
|
"""
|
||||||
|
Return proposer and inclusion delay micro-rewards/penalties for each validator.
|
||||||
|
"""
|
||||||
|
rewards = [Gwei(0) for _ in range(len(state.validators))]
|
||||||
|
matching_source_attestations = get_matching_source_attestations(state, get_previous_epoch(state))
|
||||||
for index in get_unslashed_attesting_indices(state, matching_source_attestations):
|
for index in get_unslashed_attesting_indices(state, matching_source_attestations):
|
||||||
attestation = min([
|
attestation = min([
|
||||||
a for a in matching_source_attestations
|
a for a in matching_source_attestations
|
||||||
|
@ -1385,19 +1431,61 @@ def get_attestation_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence
|
||||||
max_attester_reward = get_base_reward(state, index) - proposer_reward
|
max_attester_reward = get_base_reward(state, index) - proposer_reward
|
||||||
rewards[index] += Gwei(max_attester_reward // attestation.inclusion_delay)
|
rewards[index] += Gwei(max_attester_reward // attestation.inclusion_delay)
|
||||||
|
|
||||||
# Inactivity penalty
|
# No penalties associated with inclusion delay
|
||||||
finality_delay = previous_epoch - state.finalized_checkpoint.epoch
|
penalties = [Gwei(0) for _ in range(len(state.validators))]
|
||||||
|
return rewards, penalties
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_inactivity_penalty_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
||||||
|
"""
|
||||||
|
Return inactivity reward/penalty deltas for each validator.
|
||||||
|
"""
|
||||||
|
penalties = [Gwei(0) for _ in range(len(state.validators))]
|
||||||
|
finality_delay = get_previous_epoch(state) - state.finalized_checkpoint.epoch
|
||||||
|
|
||||||
if finality_delay > MIN_EPOCHS_TO_INACTIVITY_PENALTY:
|
if finality_delay > MIN_EPOCHS_TO_INACTIVITY_PENALTY:
|
||||||
|
matching_target_attestations = get_matching_target_attestations(state, get_previous_epoch(state))
|
||||||
matching_target_attesting_indices = get_unslashed_attesting_indices(state, matching_target_attestations)
|
matching_target_attesting_indices = get_unslashed_attesting_indices(state, matching_target_attestations)
|
||||||
for index in eligible_validator_indices:
|
for index in get_eligible_validator_indices(state):
|
||||||
penalties[index] += Gwei(BASE_REWARDS_PER_EPOCH * get_base_reward(state, index))
|
penalties[index] += Gwei(BASE_REWARDS_PER_EPOCH * get_base_reward(state, index))
|
||||||
if index not in matching_target_attesting_indices:
|
if index not in matching_target_attesting_indices:
|
||||||
effective_balance = state.validators[index].effective_balance
|
effective_balance = state.validators[index].effective_balance
|
||||||
penalties[index] += Gwei(effective_balance * finality_delay // INACTIVITY_PENALTY_QUOTIENT)
|
penalties[index] += Gwei(effective_balance * finality_delay // INACTIVITY_PENALTY_QUOTIENT)
|
||||||
|
|
||||||
|
# No rewards associated with inactivity penalties
|
||||||
|
rewards = [Gwei(0) for _ in range(len(state.validators))]
|
||||||
return rewards, penalties
|
return rewards, penalties
|
||||||
```
|
```
|
||||||
|
|
||||||
|
##### `get_attestation_deltas`
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_attestation_deltas(state: BeaconState) -> Tuple[Sequence[Gwei], Sequence[Gwei]]:
|
||||||
|
"""
|
||||||
|
Return attestation reward/penalty deltas for each validator.
|
||||||
|
"""
|
||||||
|
source_rewards, source_penalties = get_source_deltas(state)
|
||||||
|
target_rewards, target_penalties = get_target_deltas(state)
|
||||||
|
head_rewards, head_penalties = get_head_deltas(state)
|
||||||
|
inclusion_delay_rewards, _ = get_inclusion_delay_deltas(state)
|
||||||
|
_, inactivity_penalties = get_inactivity_penalty_deltas(state)
|
||||||
|
|
||||||
|
rewards = [
|
||||||
|
source_rewards[i] + target_rewards[i] + head_rewards[i] + inclusion_delay_rewards[i]
|
||||||
|
for i in range(len(state.validators))
|
||||||
|
]
|
||||||
|
|
||||||
|
penalties = [
|
||||||
|
source_penalties[i] + target_penalties[i] + head_penalties[i] + inactivity_penalties[i]
|
||||||
|
for i in range(len(state.validators))
|
||||||
|
]
|
||||||
|
|
||||||
|
return rewards, penalties
|
||||||
|
```
|
||||||
|
|
||||||
|
##### `process_rewards_and_penalties`
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def process_rewards_and_penalties(state: BeaconState) -> None:
|
def process_rewards_and_penalties(state: BeaconState) -> None:
|
||||||
if get_current_epoch(state) == GENESIS_EPOCH:
|
if get_current_epoch(state) == GENESIS_EPOCH:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from eth2spec.test.context import expect_assertion_error, PHASE0, PHASE1
|
from eth2spec.test.context import expect_assertion_error, PHASE0, PHASE1
|
||||||
from eth2spec.test.helpers.state import state_transition_and_sign_block, next_slot, transition_to
|
from eth2spec.test.helpers.state import state_transition_and_sign_block, next_epoch, next_slot, transition_to
|
||||||
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
|
from eth2spec.test.helpers.block import build_empty_block_for_next_slot
|
||||||
from eth2spec.test.helpers.keys import privkeys
|
from eth2spec.test.helpers.keys import privkeys
|
||||||
from eth2spec.utils import bls
|
from eth2spec.utils import bls
|
||||||
|
@ -323,6 +323,37 @@ def next_epoch_with_attestations(spec,
|
||||||
return state, signed_blocks, post_state
|
return state, signed_blocks, post_state
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_state_with_full_attestations(spec, state, empty=False):
|
||||||
|
"""
|
||||||
|
Fill ``state`` with maximally full attestations.
|
||||||
|
Move to the start of the next epoch to ensure full epoch worth.
|
||||||
|
"""
|
||||||
|
# Go to start of next epoch to ensure can have full participation
|
||||||
|
next_epoch(spec, state)
|
||||||
|
|
||||||
|
start_slot = state.slot
|
||||||
|
start_epoch = spec.get_current_epoch(state)
|
||||||
|
next_epoch_start_slot = spec.compute_start_slot_at_epoch(start_epoch + 1)
|
||||||
|
attestations = []
|
||||||
|
for _ in range(spec.SLOTS_PER_EPOCH + spec.MIN_ATTESTATION_INCLUSION_DELAY):
|
||||||
|
# create an attestation for each index in each slot in epoch
|
||||||
|
if state.slot < next_epoch_start_slot:
|
||||||
|
for committee_index in range(spec.get_committee_count_at_slot(state, state.slot)):
|
||||||
|
attestation = get_valid_attestation(spec, state, index=committee_index, empty=empty, signed=True)
|
||||||
|
attestations.append(attestation)
|
||||||
|
# fill each created slot in state after inclusion delay
|
||||||
|
if state.slot >= start_slot + spec.MIN_ATTESTATION_INCLUSION_DELAY:
|
||||||
|
inclusion_slot = state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||||
|
include_attestations = [att for att in attestations if att.data.slot == inclusion_slot]
|
||||||
|
add_attestations_to_state(spec, state, include_attestations, state.slot)
|
||||||
|
next_slot(spec, state)
|
||||||
|
|
||||||
|
assert state.slot == next_epoch_start_slot + spec.MIN_ATTESTATION_INCLUSION_DELAY
|
||||||
|
assert len(state.previous_epoch_attestations) == len(attestations)
|
||||||
|
|
||||||
|
return attestations
|
||||||
|
|
||||||
|
|
||||||
def fill_block_shard_transitions_by_attestations(spec, state, block):
|
def fill_block_shard_transitions_by_attestations(spec, state, block):
|
||||||
block.body.shard_transitions = [spec.ShardTransition()] * spec.MAX_SHARDS
|
block.body.shard_transitions = [spec.ShardTransition()] * spec.MAX_SHARDS
|
||||||
for attestation in block.body.attestations:
|
for attestation in block.body.attestations:
|
||||||
|
|
|
@ -0,0 +1,208 @@
|
||||||
|
from random import Random
|
||||||
|
|
||||||
|
from eth2spec.phase0 import spec as spec_phase0
|
||||||
|
from eth2spec.test.helpers.attestations import prepare_state_with_full_attestations
|
||||||
|
from eth2spec.test.helpers.state import next_epoch
|
||||||
|
from eth2spec.utils.ssz.ssz_typing import Container, uint64, List
|
||||||
|
|
||||||
|
|
||||||
|
class Deltas(Container):
|
||||||
|
rewards: List[uint64, spec_phase0.VALIDATOR_REGISTRY_LIMIT]
|
||||||
|
penalties: List[uint64, spec_phase0.VALIDATOR_REGISTRY_LIMIT]
|
||||||
|
|
||||||
|
|
||||||
|
def has_enough_for_reward(spec, state, index):
|
||||||
|
"""
|
||||||
|
Check if base_reward will be non-zero.
|
||||||
|
|
||||||
|
At very low balances, it is possible for a validator have a positive effective_balance
|
||||||
|
but a zero base reward.
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
state.validators[index].effective_balance * spec.BASE_REWARD_FACTOR
|
||||||
|
> spec.integer_squareroot(spec.get_total_active_balance(state)) // spec.BASE_REWARDS_PER_EPOCH
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def run_attestation_component_deltas(spec, state, component_delta_fn, matching_att_fn):
|
||||||
|
"""
|
||||||
|
Run ``component_delta_fn``, yielding:
|
||||||
|
- pre-state ('pre')
|
||||||
|
- deltas ('deltas')
|
||||||
|
"""
|
||||||
|
yield 'pre', state
|
||||||
|
|
||||||
|
rewards, penalties = component_delta_fn(state)
|
||||||
|
|
||||||
|
yield 'deltas', Deltas(rewards=rewards, penalties=penalties)
|
||||||
|
|
||||||
|
matching_attestations = matching_att_fn(state, spec.get_previous_epoch(state))
|
||||||
|
matching_indices = spec.get_unslashed_attesting_indices(state, matching_attestations)
|
||||||
|
eligible_indices = spec.get_eligible_validator_indices(state)
|
||||||
|
for index in range(len(state.validators)):
|
||||||
|
if index not in eligible_indices:
|
||||||
|
assert rewards[index] == 0
|
||||||
|
assert penalties[index] == 0
|
||||||
|
continue
|
||||||
|
|
||||||
|
validator = state.validators[index]
|
||||||
|
enough_for_reward = has_enough_for_reward(spec, state, index)
|
||||||
|
if index in matching_indices and not validator.slashed:
|
||||||
|
if enough_for_reward:
|
||||||
|
assert rewards[index] > 0
|
||||||
|
else:
|
||||||
|
assert rewards[index] == 0
|
||||||
|
assert penalties[index] == 0
|
||||||
|
else:
|
||||||
|
assert rewards[index] == 0
|
||||||
|
if enough_for_reward:
|
||||||
|
assert penalties[index] > 0
|
||||||
|
else:
|
||||||
|
assert penalties[index] == 0
|
||||||
|
|
||||||
|
|
||||||
|
def exit_random_validators(spec, state, rng):
|
||||||
|
if spec.get_current_epoch(state) < 5:
|
||||||
|
# Move epochs forward to allow for some validators already exited/withdrawable
|
||||||
|
for _ in range(5):
|
||||||
|
next_epoch(spec, state)
|
||||||
|
|
||||||
|
current_epoch = spec.get_current_epoch(state)
|
||||||
|
# Exit ~1/2 of validators
|
||||||
|
for validator in state.validators:
|
||||||
|
if rng.choice([True, False]):
|
||||||
|
continue
|
||||||
|
|
||||||
|
validator.exit_epoch = rng.choice([current_epoch - 1, current_epoch - 2, current_epoch - 3])
|
||||||
|
# ~1/2 are withdrawable
|
||||||
|
if rng.choice([True, False]):
|
||||||
|
validator.withdrawable_epoch = current_epoch
|
||||||
|
else:
|
||||||
|
validator.withdrawable_epoch = current_epoch + 1
|
||||||
|
|
||||||
|
|
||||||
|
def slash_random_validators(spec, state, rng):
|
||||||
|
# Slash ~1/2 of validators
|
||||||
|
for validator in state.validators:
|
||||||
|
validator.slashed = rng.choice([True, False])
|
||||||
|
|
||||||
|
|
||||||
|
def run_test_empty(spec, state, runner):
|
||||||
|
# Do not add any attestations to state
|
||||||
|
|
||||||
|
yield from runner(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
def run_test_full_all_correct(spec, state, runner):
|
||||||
|
prepare_state_with_full_attestations(spec, state)
|
||||||
|
|
||||||
|
yield from runner(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
def run_test_full_but_partial_participation(spec, state, runner, rng=Random(5522)):
|
||||||
|
prepare_state_with_full_attestations(spec, state)
|
||||||
|
|
||||||
|
for a in state.previous_epoch_attestations:
|
||||||
|
a.aggregation_bits = [rng.choice([True, False]) for _ in a.aggregation_bits]
|
||||||
|
|
||||||
|
yield from runner(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
def run_test_partial(spec, state, fraction_filled, runner):
|
||||||
|
prepare_state_with_full_attestations(spec, state)
|
||||||
|
|
||||||
|
# Remove portion of attestations
|
||||||
|
num_attestations = int(len(state.previous_epoch_attestations) * fraction_filled)
|
||||||
|
state.previous_epoch_attestations = state.previous_epoch_attestations[:num_attestations]
|
||||||
|
|
||||||
|
yield from runner(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
def run_test_half_full(spec, state, runner):
|
||||||
|
yield from run_test_partial(spec, state, 0.5, runner)
|
||||||
|
|
||||||
|
|
||||||
|
def run_test_one_attestation_one_correct(spec, state, runner):
|
||||||
|
prepare_state_with_full_attestations(spec, state)
|
||||||
|
|
||||||
|
# Remove all attestations except for the first one
|
||||||
|
state.previous_epoch_attestations = state.previous_epoch_attestations[:1]
|
||||||
|
|
||||||
|
yield from runner(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
def run_test_with_exited_validators(spec, state, runner, rng=Random(1337)):
|
||||||
|
exit_random_validators(spec, state, rng)
|
||||||
|
prepare_state_with_full_attestations(spec, state)
|
||||||
|
|
||||||
|
yield from runner(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
def run_test_with_slashed_validators(spec, state, runner, rng=Random(3322)):
|
||||||
|
exit_random_validators(spec, state, rng)
|
||||||
|
slash_random_validators(spec, state, rng)
|
||||||
|
|
||||||
|
prepare_state_with_full_attestations(spec, state)
|
||||||
|
|
||||||
|
yield from runner(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
def run_test_some_very_low_effective_balances_that_attested(spec, state, runner):
|
||||||
|
state.balances
|
||||||
|
prepare_state_with_full_attestations(spec, state)
|
||||||
|
|
||||||
|
# Set some balances to be very low (including 0)
|
||||||
|
assert len(state.validators) >= 5
|
||||||
|
for i, index in enumerate(range(5)):
|
||||||
|
state.validators[index].effective_balance = i
|
||||||
|
|
||||||
|
yield from runner(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
def run_test_some_very_low_effective_balances_that_did_not_attest(spec, state, runner):
|
||||||
|
prepare_state_with_full_attestations(spec, state)
|
||||||
|
|
||||||
|
# Remove attestation
|
||||||
|
attestation = state.previous_epoch_attestations[0]
|
||||||
|
state.previous_epoch_attestations = state.previous_epoch_attestations[1:]
|
||||||
|
# Set removed indices effective balance to very low amount
|
||||||
|
indices = spec.get_unslashed_attesting_indices(state, [attestation])
|
||||||
|
for i, index in enumerate(indices):
|
||||||
|
state.validators[index].effective_balance = i
|
||||||
|
|
||||||
|
yield from runner(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
def run_test_full_fraction_incorrect(spec, state, correct_target, correct_head, fraction_incorrect, runner):
|
||||||
|
prepare_state_with_full_attestations(spec, state)
|
||||||
|
|
||||||
|
# Make fraction_incorrect of pending attestations have bad target/head as specified
|
||||||
|
num_incorrect = int(fraction_incorrect * len(state.previous_epoch_attestations))
|
||||||
|
for pending_attestation in state.previous_epoch_attestations[:num_incorrect]:
|
||||||
|
if not correct_target:
|
||||||
|
pending_attestation.data.target.root = b'\x55' * 32
|
||||||
|
if not correct_head:
|
||||||
|
pending_attestation.data.beacon_block_root = b'\x66' * 32
|
||||||
|
|
||||||
|
yield from runner(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
def run_test_full_random(spec, state, runner, rng=Random(8020)):
|
||||||
|
exit_random_validators(spec, state, rng)
|
||||||
|
slash_random_validators(spec, state, rng)
|
||||||
|
|
||||||
|
prepare_state_with_full_attestations(spec, state)
|
||||||
|
|
||||||
|
for pending_attestation in state.previous_epoch_attestations:
|
||||||
|
# ~1/3 have bad target
|
||||||
|
if rng.randint(0, 2) == 0:
|
||||||
|
pending_attestation.data.target.root = b'\x55' * 32
|
||||||
|
# ~1/3 have bad head
|
||||||
|
if rng.randint(0, 2) == 0:
|
||||||
|
pending_attestation.data.beacon_block_root = b'\x66' * 32
|
||||||
|
# ~50% participation
|
||||||
|
pending_attestation.aggregation_bits = [rng.choice([True, False]) for _ in pending_attestation.aggregation_bits]
|
||||||
|
# Random inclusion delay
|
||||||
|
pending_attestation.inclusion_delay = rng.randint(1, spec.SLOTS_PER_EPOCH)
|
||||||
|
|
||||||
|
yield from runner(spec, state)
|
|
@ -12,6 +12,7 @@ from eth2spec.test.helpers.state import (
|
||||||
from eth2spec.test.helpers.attestations import (
|
from eth2spec.test.helpers.attestations import (
|
||||||
add_attestations_to_state,
|
add_attestations_to_state,
|
||||||
get_valid_attestation,
|
get_valid_attestation,
|
||||||
|
prepare_state_with_full_attestations,
|
||||||
)
|
)
|
||||||
from eth2spec.test.helpers.attester_slashings import get_indexed_attestation_participants
|
from eth2spec.test.helpers.attester_slashings import get_indexed_attestation_participants
|
||||||
from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_with
|
from eth2spec.test.phase_0.epoch_processing.run_epoch_process_base import run_epoch_processing_with
|
||||||
|
@ -21,33 +22,6 @@ 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 prepare_state_with_full_attestations(spec, state, empty=False):
|
|
||||||
# Go to start of next epoch to ensure can have full participation
|
|
||||||
next_epoch(spec, state)
|
|
||||||
|
|
||||||
start_slot = state.slot
|
|
||||||
start_epoch = spec.get_current_epoch(state)
|
|
||||||
next_epoch_start_slot = spec.compute_start_slot_at_epoch(start_epoch + 1)
|
|
||||||
attestations = []
|
|
||||||
for _ in range(spec.SLOTS_PER_EPOCH + spec.MIN_ATTESTATION_INCLUSION_DELAY):
|
|
||||||
# create an attestation for each index in each slot in epoch
|
|
||||||
if state.slot < next_epoch_start_slot:
|
|
||||||
for committee_index in range(spec.get_committee_count_at_slot(state, state.slot)):
|
|
||||||
attestation = get_valid_attestation(spec, state, index=committee_index, empty=empty, signed=True)
|
|
||||||
attestations.append(attestation)
|
|
||||||
# fill each created slot in state after inclusion delay
|
|
||||||
if state.slot >= start_slot + spec.MIN_ATTESTATION_INCLUSION_DELAY:
|
|
||||||
inclusion_slot = state.slot - spec.MIN_ATTESTATION_INCLUSION_DELAY
|
|
||||||
include_attestations = [att for att in attestations if att.data.slot == inclusion_slot]
|
|
||||||
add_attestations_to_state(spec, state, include_attestations, state.slot)
|
|
||||||
next_slot(spec, state)
|
|
||||||
|
|
||||||
assert state.slot == next_epoch_start_slot + spec.MIN_ATTESTATION_INCLUSION_DELAY
|
|
||||||
assert len(state.previous_epoch_attestations) == len(attestations)
|
|
||||||
|
|
||||||
return attestations
|
|
||||||
|
|
||||||
|
|
||||||
@with_phases(['phase0'])
|
@with_phases(['phase0'])
|
||||||
@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):
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
from eth2spec.test.context import with_all_phases, spec_state_test
|
||||||
|
from eth2spec.test.helpers.rewards import run_attestation_component_deltas
|
||||||
|
import eth2spec.test.helpers.rewards as rewards_helpers
|
||||||
|
|
||||||
|
|
||||||
|
def run_get_head_deltas(spec, state):
|
||||||
|
"""
|
||||||
|
Run ``get_head_deltas``, yielding:
|
||||||
|
- pre-state ('pre')
|
||||||
|
- deltas ('deltas')
|
||||||
|
"""
|
||||||
|
|
||||||
|
yield from run_attestation_component_deltas(
|
||||||
|
spec,
|
||||||
|
state,
|
||||||
|
spec.get_head_deltas,
|
||||||
|
spec.get_matching_head_attestations,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_empty(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_empty(spec, state, run_get_head_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_all_correct(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_all_correct(spec, state, run_get_head_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_half_full(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_half_full(spec, state, run_get_head_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_but_partial_participation(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_but_partial_participation(spec, state, run_get_head_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_one_attestation_one_correct(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_one_attestation_one_correct(spec, state, run_get_head_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_with_exited_validators(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_with_exited_validators(spec, state, run_get_head_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_with_slashed_validators(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_with_slashed_validators(spec, state, run_get_head_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_some_very_low_effective_balances_that_attested(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_some_very_low_effective_balances_that_attested(spec, state, run_get_head_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_some_very_low_effective_balances_that_did_not_attest(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_some_very_low_effective_balances_that_did_not_attest(
|
||||||
|
spec,
|
||||||
|
state,
|
||||||
|
run_get_head_deltas,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_half_correct_target_incorrect_head(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_fraction_incorrect(
|
||||||
|
spec, state,
|
||||||
|
correct_target=True,
|
||||||
|
correct_head=False,
|
||||||
|
fraction_incorrect=0.5,
|
||||||
|
runner=run_get_head_deltas
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_correct_target_incorrect_head(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_fraction_incorrect(
|
||||||
|
spec, state,
|
||||||
|
correct_target=True,
|
||||||
|
correct_head=False,
|
||||||
|
fraction_incorrect=1.0,
|
||||||
|
runner=run_get_head_deltas
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_half_incorrect_target_incorrect_head(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_fraction_incorrect(
|
||||||
|
spec, state,
|
||||||
|
correct_target=False,
|
||||||
|
correct_head=False,
|
||||||
|
fraction_incorrect=0.5,
|
||||||
|
runner=run_get_head_deltas
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_half_incorrect_target_correct_head(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_fraction_incorrect(
|
||||||
|
spec, state,
|
||||||
|
correct_target=False,
|
||||||
|
correct_head=True,
|
||||||
|
fraction_incorrect=0.5,
|
||||||
|
runner=run_get_head_deltas
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_random(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state, run_get_head_deltas)
|
|
@ -0,0 +1,210 @@
|
||||||
|
from eth2spec.test.context import with_all_phases, spec_state_test
|
||||||
|
from eth2spec.test.helpers.rewards import has_enough_for_reward
|
||||||
|
from eth2spec.test.helpers.state import next_epoch
|
||||||
|
from eth2spec.test.helpers.rewards import Deltas
|
||||||
|
import eth2spec.test.helpers.rewards as rewards_helpers
|
||||||
|
|
||||||
|
|
||||||
|
def run_get_inactivity_penalty_deltas(spec, state):
|
||||||
|
"""
|
||||||
|
Run ``get_inactivity_penalty_deltas``, yielding:
|
||||||
|
- pre-state ('pre')
|
||||||
|
- deltas ('deltas')
|
||||||
|
"""
|
||||||
|
|
||||||
|
yield 'pre', state
|
||||||
|
|
||||||
|
rewards, penalties = spec.get_inactivity_penalty_deltas(state)
|
||||||
|
|
||||||
|
yield 'deltas', Deltas(rewards=rewards, penalties=penalties)
|
||||||
|
|
||||||
|
matching_attestations = spec.get_matching_target_attestations(state, spec.get_previous_epoch(state))
|
||||||
|
matching_attesting_indices = spec.get_unslashed_attesting_indices(state, matching_attestations)
|
||||||
|
|
||||||
|
finality_delay = spec.get_previous_epoch(state) - state.finalized_checkpoint.epoch
|
||||||
|
eligible_indices = spec.get_eligible_validator_indices(state)
|
||||||
|
for index in range(len(state.validators)):
|
||||||
|
assert rewards[index] == 0
|
||||||
|
if index not in eligible_indices:
|
||||||
|
assert penalties[index] == 0
|
||||||
|
continue
|
||||||
|
|
||||||
|
if finality_delay > spec.MIN_EPOCHS_TO_INACTIVITY_PENALTY:
|
||||||
|
base_penalty = spec.BASE_REWARDS_PER_EPOCH * spec.get_base_reward(state, index)
|
||||||
|
if not has_enough_for_reward(spec, state, index):
|
||||||
|
assert penalties[index] == 0
|
||||||
|
elif index in matching_attesting_indices:
|
||||||
|
assert penalties[index] == base_penalty
|
||||||
|
else:
|
||||||
|
assert penalties[index] > base_penalty
|
||||||
|
else:
|
||||||
|
assert penalties[index] == 0
|
||||||
|
|
||||||
|
|
||||||
|
def transition_state_to_leak(spec, state, epochs=None):
|
||||||
|
if epochs is None:
|
||||||
|
epochs = spec.MIN_EPOCHS_TO_INACTIVITY_PENALTY
|
||||||
|
assert epochs >= spec.MIN_EPOCHS_TO_INACTIVITY_PENALTY
|
||||||
|
|
||||||
|
for _ in range(epochs):
|
||||||
|
next_epoch(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_empty_no_leak(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_empty(spec, state, run_get_inactivity_penalty_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_empty_leak(spec, state):
|
||||||
|
transition_state_to_leak(spec, state)
|
||||||
|
yield from rewards_helpers.run_test_empty(spec, state, run_get_inactivity_penalty_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_no_leak(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_all_correct(spec, state, run_get_inactivity_penalty_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_leak(spec, state):
|
||||||
|
transition_state_to_leak(spec, state)
|
||||||
|
yield from rewards_helpers.run_test_full_all_correct(spec, state, run_get_inactivity_penalty_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_half_full_no_leak(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_half_full(spec, state, run_get_inactivity_penalty_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_half_full_leak(spec, state):
|
||||||
|
transition_state_to_leak(spec, state)
|
||||||
|
yield from rewards_helpers.run_test_half_full(spec, state, run_get_inactivity_penalty_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_quarter_full_no_leak(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_partial(spec, state, 0.25, run_get_inactivity_penalty_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_quarter_full_leak(spec, state):
|
||||||
|
transition_state_to_leak(spec, state)
|
||||||
|
yield from rewards_helpers.run_test_partial(spec, state, 0.25, run_get_inactivity_penalty_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_but_partial_participation_no_leak(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_but_partial_participation(spec, state, run_get_inactivity_penalty_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_but_partial_participation_leak(spec, state):
|
||||||
|
transition_state_to_leak(spec, state)
|
||||||
|
yield from rewards_helpers.run_test_full_but_partial_participation(spec, state, run_get_inactivity_penalty_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_with_exited_validators_no_leak(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_with_exited_validators(spec, state, run_get_inactivity_penalty_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_with_exited_validators_leak(spec, state):
|
||||||
|
transition_state_to_leak(spec, state)
|
||||||
|
yield from rewards_helpers.run_test_with_exited_validators(spec, state, run_get_inactivity_penalty_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_with_slashed_validators_no_leak(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_with_slashed_validators(spec, state, run_get_inactivity_penalty_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_with_slashed_validators_leak(spec, state):
|
||||||
|
transition_state_to_leak(spec, state)
|
||||||
|
yield from rewards_helpers.run_test_with_slashed_validators(spec, state, run_get_inactivity_penalty_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_some_very_low_effective_balances_that_attested_no_leak(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_some_very_low_effective_balances_that_attested(
|
||||||
|
spec,
|
||||||
|
state,
|
||||||
|
run_get_inactivity_penalty_deltas,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_some_very_low_effective_balances_that_attested_leak(spec, state):
|
||||||
|
transition_state_to_leak(spec, state)
|
||||||
|
yield from rewards_helpers.run_test_some_very_low_effective_balances_that_attested(
|
||||||
|
spec,
|
||||||
|
state,
|
||||||
|
run_get_inactivity_penalty_deltas,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_some_very_low_effective_balances_that_did_not_attest_no_leak(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_some_very_low_effective_balances_that_did_not_attest(
|
||||||
|
spec,
|
||||||
|
state,
|
||||||
|
run_get_inactivity_penalty_deltas,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_some_very_low_effective_balances_that_did_not_attest_leak(spec, state):
|
||||||
|
transition_state_to_leak(spec, state)
|
||||||
|
yield from rewards_helpers.run_test_some_very_low_effective_balances_that_did_not_attest(
|
||||||
|
spec,
|
||||||
|
state,
|
||||||
|
run_get_inactivity_penalty_deltas,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_random_no_leak(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state, run_get_inactivity_penalty_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_random_leak(spec, state):
|
||||||
|
transition_state_to_leak(spec, state)
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state, run_get_inactivity_penalty_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_random_five_epoch_leak(spec, state):
|
||||||
|
transition_state_to_leak(spec, state, epochs=5)
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state, run_get_inactivity_penalty_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_random_ten_epoch_leak(spec, state):
|
||||||
|
transition_state_to_leak(spec, state, epochs=10)
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state, run_get_inactivity_penalty_deltas)
|
|
@ -0,0 +1,207 @@
|
||||||
|
from random import Random
|
||||||
|
|
||||||
|
from eth2spec.test.context import with_all_phases, spec_state_test
|
||||||
|
from eth2spec.test.helpers.attestations import prepare_state_with_full_attestations
|
||||||
|
from eth2spec.test.helpers.rewards import Deltas, has_enough_for_reward
|
||||||
|
import eth2spec.test.helpers.rewards as rewards_helpers
|
||||||
|
|
||||||
|
|
||||||
|
def run_get_inclusion_delay_deltas(spec, state):
|
||||||
|
"""
|
||||||
|
Run ``get_inclusion_delay_deltas``, yielding:
|
||||||
|
- pre-state ('pre')
|
||||||
|
- deltas ('deltas')
|
||||||
|
"""
|
||||||
|
|
||||||
|
yield 'pre', state
|
||||||
|
|
||||||
|
rewards, penalties = spec.get_inclusion_delay_deltas(state)
|
||||||
|
|
||||||
|
yield 'deltas', Deltas(rewards=rewards, penalties=penalties)
|
||||||
|
|
||||||
|
eligible_attestations = spec.get_matching_source_attestations(state, spec.get_previous_epoch(state))
|
||||||
|
attesting_indices = spec.get_unslashed_attesting_indices(state, eligible_attestations)
|
||||||
|
|
||||||
|
rewarded_indices = set()
|
||||||
|
rewarded_proposer_indices = set()
|
||||||
|
# Ensure attesters with enough balance are rewarded for attestations
|
||||||
|
# Track those that are rewarded and track proposers that should be rewarded
|
||||||
|
for index in range(len(state.validators)):
|
||||||
|
if index in attesting_indices and has_enough_for_reward(spec, state, index):
|
||||||
|
assert rewards[index] > 0
|
||||||
|
rewarded_indices.add(index)
|
||||||
|
|
||||||
|
# Track proposer of earliest included attestation for the validator defined by index
|
||||||
|
earliest_attestation = min([
|
||||||
|
a for a in eligible_attestations
|
||||||
|
if index in spec.get_attesting_indices(state, a.data, a.aggregation_bits)
|
||||||
|
], key=lambda a: a.inclusion_delay)
|
||||||
|
rewarded_proposer_indices.add(earliest_attestation.proposer_index)
|
||||||
|
|
||||||
|
# Ensure all expected proposers have been rewarded
|
||||||
|
# Track rewarde indices
|
||||||
|
proposing_indices = [a.proposer_index for a in eligible_attestations]
|
||||||
|
for index in proposing_indices:
|
||||||
|
if index in rewarded_proposer_indices:
|
||||||
|
assert rewards[index] > 0
|
||||||
|
rewarded_indices.add(index)
|
||||||
|
|
||||||
|
# Ensure all expected non-rewarded indices received no reward
|
||||||
|
for index in range(len(state.validators)):
|
||||||
|
assert penalties[index] == 0
|
||||||
|
if index not in rewarded_indices:
|
||||||
|
assert rewards[index] == 0
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_empty(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_empty(spec, state, run_get_inclusion_delay_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_all_correct(spec, state, run_get_inclusion_delay_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_half_full(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_half_full(spec, state, run_get_inclusion_delay_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_quarter_full(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_partial(spec, state, 0.25, run_get_inclusion_delay_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_but_partial_participation(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_but_partial_participation(spec, state, run_get_inclusion_delay_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_with_exited_validators(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_with_exited_validators(spec, state, run_get_inclusion_delay_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_with_slashed_validators(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_with_slashed_validators(spec, state, run_get_inclusion_delay_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_some_very_low_effective_balances_that_attested(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_some_very_low_effective_balances_that_attested(
|
||||||
|
spec,
|
||||||
|
state,
|
||||||
|
run_get_inclusion_delay_deltas
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_random(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state, run_get_inclusion_delay_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_delay_one_slot(spec, state):
|
||||||
|
prepare_state_with_full_attestations(spec, state)
|
||||||
|
for a in state.previous_epoch_attestations:
|
||||||
|
a.inclusion_delay += 1
|
||||||
|
|
||||||
|
yield from run_get_inclusion_delay_deltas(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_delay_max_slots(spec, state):
|
||||||
|
prepare_state_with_full_attestations(spec, state)
|
||||||
|
for a in state.previous_epoch_attestations:
|
||||||
|
a.inclusion_delay += spec.SLOTS_PER_EPOCH
|
||||||
|
|
||||||
|
yield from run_get_inclusion_delay_deltas(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_mixed_delay(spec, state):
|
||||||
|
rng = Random(1234)
|
||||||
|
|
||||||
|
prepare_state_with_full_attestations(spec, state)
|
||||||
|
for a in state.previous_epoch_attestations:
|
||||||
|
a.inclusion_delay = rng.randint(1, spec.SLOTS_PER_EPOCH)
|
||||||
|
|
||||||
|
yield from run_get_inclusion_delay_deltas(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_proposer_not_in_attestations(spec, state):
|
||||||
|
prepare_state_with_full_attestations(spec, state)
|
||||||
|
|
||||||
|
# Get an attestation where the proposer is not in the committee
|
||||||
|
non_proposer_attestations = []
|
||||||
|
for a in state.previous_epoch_attestations:
|
||||||
|
if a.proposer_index not in spec.get_unslashed_attesting_indices(state, [a]):
|
||||||
|
non_proposer_attestations.append(a)
|
||||||
|
|
||||||
|
assert any(non_proposer_attestations)
|
||||||
|
state.previous_epoch_attestations = non_proposer_attestations
|
||||||
|
|
||||||
|
yield from run_get_inclusion_delay_deltas(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_duplicate_attestations_at_later_slots(spec, state):
|
||||||
|
prepare_state_with_full_attestations(spec, state)
|
||||||
|
|
||||||
|
# Remove 2/3 of attestations to make it more interesting
|
||||||
|
num_attestations = int(len(state.previous_epoch_attestations) * 0.33)
|
||||||
|
state.previous_epoch_attestations = state.previous_epoch_attestations[:num_attestations]
|
||||||
|
|
||||||
|
# Get map of the proposer at each slot to make valid-looking duplicate attestations
|
||||||
|
per_slot_proposers = {
|
||||||
|
(a.data.slot + a.inclusion_delay): a.proposer_index
|
||||||
|
for a in state.previous_epoch_attestations
|
||||||
|
}
|
||||||
|
max_slot = max([a.data.slot + a.inclusion_delay for a in state.previous_epoch_attestations])
|
||||||
|
later_attestations = []
|
||||||
|
for a in state.previous_epoch_attestations:
|
||||||
|
# Only have proposers for previous epoch so do not create later
|
||||||
|
# duplicate if slot exceeds the max slot in previous_epoch_attestations
|
||||||
|
if a.data.slot + a.inclusion_delay >= max_slot:
|
||||||
|
continue
|
||||||
|
later_a = a.copy()
|
||||||
|
later_a.inclusion_delay += 1
|
||||||
|
later_a.proposer_index = per_slot_proposers[later_a.data.slot + later_a.inclusion_delay]
|
||||||
|
later_attestations.append(later_a)
|
||||||
|
|
||||||
|
assert any(later_attestations)
|
||||||
|
|
||||||
|
state.previous_epoch_attestations = sorted(
|
||||||
|
state.previous_epoch_attestations + later_attestations,
|
||||||
|
key=lambda a: a.data.slot + a.inclusion_delay
|
||||||
|
)
|
||||||
|
|
||||||
|
yield from run_get_inclusion_delay_deltas(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_all_balances_too_low_for_reward(spec, state):
|
||||||
|
prepare_state_with_full_attestations(spec, state)
|
||||||
|
|
||||||
|
for index in range(len(state.validators)):
|
||||||
|
state.validators[index].effective_balance = 10
|
||||||
|
|
||||||
|
yield from run_get_inclusion_delay_deltas(spec, state)
|
|
@ -0,0 +1,141 @@
|
||||||
|
from eth2spec.test.context import with_all_phases, spec_state_test
|
||||||
|
from eth2spec.test.helpers.rewards import run_attestation_component_deltas
|
||||||
|
import eth2spec.test.helpers.rewards as rewards_helpers
|
||||||
|
|
||||||
|
|
||||||
|
def run_get_source_deltas(spec, state):
|
||||||
|
"""
|
||||||
|
Run ``get_source_deltas``, yielding:
|
||||||
|
- pre-state ('pre')
|
||||||
|
- deltas ('deltas')
|
||||||
|
"""
|
||||||
|
|
||||||
|
yield from run_attestation_component_deltas(
|
||||||
|
spec,
|
||||||
|
state,
|
||||||
|
spec.get_source_deltas,
|
||||||
|
spec.get_matching_source_attestations,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_empty(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_empty(spec, state, run_get_source_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_all_correct(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_all_correct(spec, state, run_get_source_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_half_full(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_half_full(spec, state, run_get_source_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_but_partial_participation(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_but_partial_participation(spec, state, run_get_source_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_one_attestation_one_correct(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_one_attestation_one_correct(spec, state, run_get_source_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_with_exited_validators(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_with_exited_validators(spec, state, run_get_source_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_with_slashed_validators(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_with_slashed_validators(spec, state, run_get_source_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_some_very_low_effective_balances_that_attested(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_some_very_low_effective_balances_that_attested(
|
||||||
|
spec,
|
||||||
|
state,
|
||||||
|
run_get_source_deltas
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_some_very_low_effective_balances_that_did_not_attest(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_some_very_low_effective_balances_that_did_not_attest(
|
||||||
|
spec,
|
||||||
|
state,
|
||||||
|
run_get_source_deltas,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# NOTE: No source incorrect tests
|
||||||
|
# All PendingAttestations in state have source validated
|
||||||
|
# We choose to keep this invariant in these tests to not force clients to test with degenerate states
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_half_correct_target_incorrect_head(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_fraction_incorrect(
|
||||||
|
spec, state,
|
||||||
|
correct_target=True,
|
||||||
|
correct_head=False,
|
||||||
|
fraction_incorrect=0.5,
|
||||||
|
runner=run_get_source_deltas
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_correct_target_incorrect_head(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_fraction_incorrect(
|
||||||
|
spec, state,
|
||||||
|
correct_target=True,
|
||||||
|
correct_head=False,
|
||||||
|
fraction_incorrect=1.0,
|
||||||
|
runner=run_get_source_deltas
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_half_incorrect_target_incorrect_head(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_fraction_incorrect(
|
||||||
|
spec, state,
|
||||||
|
correct_target=False,
|
||||||
|
correct_head=False,
|
||||||
|
fraction_incorrect=0.5,
|
||||||
|
runner=run_get_source_deltas
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_half_incorrect_target_correct_head(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_fraction_incorrect(
|
||||||
|
spec, state,
|
||||||
|
correct_target=False,
|
||||||
|
correct_head=True,
|
||||||
|
fraction_incorrect=0.5,
|
||||||
|
runner=run_get_source_deltas
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_random(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state, run_get_source_deltas)
|
|
@ -0,0 +1,128 @@
|
||||||
|
from eth2spec.test.context import with_all_phases, spec_state_test
|
||||||
|
from eth2spec.test.helpers.rewards import run_attestation_component_deltas
|
||||||
|
import eth2spec.test.helpers.rewards as rewards_helpers
|
||||||
|
|
||||||
|
|
||||||
|
def run_get_target_deltas(spec, state):
|
||||||
|
"""
|
||||||
|
Run ``get_target_deltas``, yielding:
|
||||||
|
- pre-state ('pre')
|
||||||
|
- deltas ('deltas')
|
||||||
|
"""
|
||||||
|
|
||||||
|
yield from run_attestation_component_deltas(
|
||||||
|
spec,
|
||||||
|
state,
|
||||||
|
spec.get_target_deltas,
|
||||||
|
spec.get_matching_target_attestations,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_empty(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_empty(spec, state, run_get_target_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_all_correct(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_all_correct(spec, state, run_get_target_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_half_full(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_half_full(spec, state, run_get_target_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_but_partial_participation(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_but_partial_participation(spec, state, run_get_target_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_one_attestation_one_correct(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_one_attestation_one_correct(spec, state, run_get_target_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_with_slashed_validators(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_with_slashed_validators(spec, state, run_get_target_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_some_very_low_effective_balances_that_attested(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_some_very_low_effective_balances_that_attested(
|
||||||
|
spec,
|
||||||
|
state,
|
||||||
|
run_get_target_deltas
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_some_very_low_effective_balances_that_did_not_attest(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_some_very_low_effective_balances_that_did_not_attest(
|
||||||
|
spec,
|
||||||
|
state,
|
||||||
|
run_get_target_deltas,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_half_correct_target_incorrect_head(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_fraction_incorrect(
|
||||||
|
spec, state,
|
||||||
|
correct_target=True,
|
||||||
|
correct_head=False,
|
||||||
|
fraction_incorrect=0.5,
|
||||||
|
runner=run_get_target_deltas
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_correct_target_incorrect_head(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_fraction_incorrect(
|
||||||
|
spec, state,
|
||||||
|
correct_target=True,
|
||||||
|
correct_head=False,
|
||||||
|
fraction_incorrect=1.0,
|
||||||
|
runner=run_get_target_deltas
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_half_incorrect_target_incorrect_head(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_fraction_incorrect(
|
||||||
|
spec, state,
|
||||||
|
correct_target=False,
|
||||||
|
correct_head=False,
|
||||||
|
fraction_incorrect=0.5,
|
||||||
|
runner=run_get_target_deltas
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_half_incorrect_target_correct_head(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_fraction_incorrect(
|
||||||
|
spec, state,
|
||||||
|
correct_target=False,
|
||||||
|
correct_head=True,
|
||||||
|
fraction_incorrect=0.5,
|
||||||
|
runner=run_get_target_deltas
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_random(spec, state):
|
||||||
|
yield from rewards_helpers.run_test_full_random(spec, state, run_get_target_deltas)
|
|
@ -0,0 +1,52 @@
|
||||||
|
# Rewards tests
|
||||||
|
|
||||||
|
The different rewards deltas sub-functions are testing individually with the test handlers, each returning the related `rewards`/`penalties`.
|
||||||
|
There is no "change" factor, the rewards/penalties outputs are pure functions with just the pre-state as input.
|
||||||
|
Hence, the format is shared between each test-handler. (See test condition documentation on how to run the tests.)
|
||||||
|
|
||||||
|
## Test case format
|
||||||
|
|
||||||
|
### `meta.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
description: string -- Optional description of test case, purely for debugging purposes.
|
||||||
|
Tests should use the directory name of the test case as identifier, not the description.
|
||||||
|
```
|
||||||
|
|
||||||
|
_Note_: No signature verification happens within rewards sub-functions. These
|
||||||
|
tests can safely be run with or without BLS enabled.
|
||||||
|
|
||||||
|
### `pre.yaml`
|
||||||
|
|
||||||
|
A YAML-encoded `BeaconState`, the state before running the rewards sub-function.
|
||||||
|
|
||||||
|
Also available as `pre.ssz`.
|
||||||
|
|
||||||
|
### `deltas.yaml`
|
||||||
|
|
||||||
|
A YAML-encoded `Deltas` representing the rewards and penalties returned by the rewards sub-function
|
||||||
|
|
||||||
|
Where `Deltas` is defined as:
|
||||||
|
```python
|
||||||
|
class Deltas(Container):
|
||||||
|
rewards: List[uint64, VALIDATOR_REGISTRY_LIMIT]
|
||||||
|
penalties: List[uint64, VALIDATOR_REGISTRY_LIMIT]
|
||||||
|
```
|
||||||
|
|
||||||
|
Also available as `deltas.ssz`.
|
||||||
|
|
||||||
|
## Condition
|
||||||
|
|
||||||
|
A handler of the `rewards` test-runner should process these cases,
|
||||||
|
calling the corresponding rewards deltas function (same name in spec).
|
||||||
|
This excludes all other parts of `process_rewards_and_penalties`
|
||||||
|
|
||||||
|
The provided pre-state is ready to be input into the designated handler.
|
||||||
|
|
||||||
|
The provided `deltas` should match the return values of the
|
||||||
|
handler. Specifically the following must hold true:
|
||||||
|
|
||||||
|
```python
|
||||||
|
deltas.rewards == handler(state)[0]
|
||||||
|
deltas.penalties == handler(state)[1]
|
||||||
|
```
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Rewards
|
||||||
|
|
||||||
|
Rewards covers the sub-functions of `process_rewards_and_penalties` for granular testing of components of the rewards function.
|
||||||
|
|
||||||
|
A rewards test-runner can consume these sub-transition test-suites,
|
||||||
|
and handle different kinds of epoch sub-transitions by processing the cases using the specified test handler.
|
||||||
|
|
||||||
|
Information on the format of the tests can be found in the [rewards test formats documentation](../../formats/rewards/README.md).
|
|
@ -0,0 +1,50 @@
|
||||||
|
from typing import Iterable
|
||||||
|
|
||||||
|
from eth2spec.phase0 import spec as spec_phase0
|
||||||
|
from eth2spec.phase1 import spec as spec_phase1
|
||||||
|
from eth2spec.test.phase_0.rewards import (
|
||||||
|
test_get_source_deltas,
|
||||||
|
test_get_target_deltas,
|
||||||
|
test_get_head_deltas,
|
||||||
|
test_get_inclusion_delay_deltas,
|
||||||
|
test_get_inactivity_penalty_deltas,
|
||||||
|
)
|
||||||
|
from gen_base import gen_runner, gen_typing
|
||||||
|
from gen_from_tests.gen import generate_from_tests
|
||||||
|
from importlib import reload
|
||||||
|
from eth2spec.config import config_util
|
||||||
|
from eth2spec.test.context import PHASE0
|
||||||
|
|
||||||
|
|
||||||
|
def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typing.TestProvider:
|
||||||
|
|
||||||
|
def prepare_fn(configs_path: str) -> str:
|
||||||
|
config_util.prepare_config(configs_path, config_name)
|
||||||
|
reload(spec_phase0)
|
||||||
|
reload(spec_phase1)
|
||||||
|
return config_name
|
||||||
|
|
||||||
|
def cases_fn() -> Iterable[gen_typing.TestCase]:
|
||||||
|
return generate_from_tests(
|
||||||
|
runner_name='rewards',
|
||||||
|
handler_name=handler_name,
|
||||||
|
src=tests_src,
|
||||||
|
fork_name=PHASE0,
|
||||||
|
)
|
||||||
|
|
||||||
|
return gen_typing.TestProvider(prepare=prepare_fn, make_cases=cases_fn)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
gen_runner.run_generator("epoch_processing", [
|
||||||
|
create_provider('get_source_deltas', test_get_source_deltas, 'minimal'),
|
||||||
|
create_provider('get_source_deltas', test_get_source_deltas, 'mainnet'),
|
||||||
|
create_provider('get_target_deltas', test_get_target_deltas, 'minimal'),
|
||||||
|
create_provider('get_target_deltas', test_get_target_deltas, 'mainnet'),
|
||||||
|
create_provider('get_head_deltas', test_get_head_deltas, 'minimal'),
|
||||||
|
create_provider('get_head_deltas', test_get_head_deltas, 'mainnet'),
|
||||||
|
create_provider('get_inclusion_delay_deltas', test_get_inclusion_delay_deltas, 'minimal'),
|
||||||
|
create_provider('get_inclusion_delay_deltas', test_get_inclusion_delay_deltas, 'mainnet'),
|
||||||
|
create_provider('get_inactivity_penalty_deltas', test_get_inactivity_penalty_deltas, 'minimal'),
|
||||||
|
create_provider('get_inactivity_penalty_deltas', test_get_inactivity_penalty_deltas, 'mainnet'),
|
||||||
|
])
|
|
@ -0,0 +1,2 @@
|
||||||
|
../../core/gen_helpers
|
||||||
|
../../../
|
Loading…
Reference in New Issue