Merge pull request #1826 from ethereum/single-rewards-gen

Single rewards generator
This commit is contained in:
Danny Ryan 2020-05-19 10:01:41 -06:00 committed by GitHub
commit 583b3322e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 630 additions and 856 deletions

View File

@ -9,6 +9,8 @@ from .utils import vector_test, with_meta_tags
from random import Random from random import Random
from typing import Any, Callable, NewType, Sequence, TypedDict, Protocol from typing import Any, Callable, NewType, Sequence, TypedDict, Protocol
from lru import LRU
from importlib import reload from importlib import reload
@ -48,28 +50,46 @@ class SpecForks(TypedDict, total=False):
PHASE1: SpecPhase1 PHASE1: SpecPhase1
def _prepare_state(balances_fn: Callable[[Any], Sequence[int]], threshold_fn: Callable[[Any], int],
spec: Spec, phases: SpecForks):
p0 = phases[PHASE0]
balances = balances_fn(p0)
activation_threshold = threshold_fn(p0)
state = create_genesis_state(spec=p0, validator_balances=balances,
activation_threshold=activation_threshold)
if spec.fork == PHASE1:
# TODO: instead of upgrading a test phase0 genesis state we can also write a phase1 state helper.
# Decide based on performance/consistency results later.
state = phases[PHASE1].upgrade_to_phase1(state)
# Shard state slot must lag behind BeaconState slot by at least 1
# Will handle this more elegantly with fork mechanics
spec.process_slots(state, state.slot + 1)
return state
_custom_state_cache_dict = LRU(size=10)
def with_custom_state(balances_fn: Callable[[Any], Sequence[int]], def with_custom_state(balances_fn: Callable[[Any], Sequence[int]],
threshold_fn: Callable[[Any], int]): threshold_fn: Callable[[Any], int]):
def deco(fn): def deco(fn):
def entry(*args, spec: Spec, phases: SpecForks, **kw): def entry(*args, spec: Spec, phases: SpecForks, **kw):
try: # make a key for the state
p0 = phases[PHASE0] # genesis fork version separates configs during test-generation runtime.
balances = balances_fn(p0) key = (spec.fork, spec.GENESIS_FORK_VERSION, spec.__file__, balances_fn, threshold_fn)
activation_threshold = threshold_fn(p0) global _custom_state_cache_dict
if key not in _custom_state_cache_dict:
state = _prepare_state(balances_fn, threshold_fn, spec, phases)
_custom_state_cache_dict[key] = state.get_backing()
state = create_genesis_state(spec=p0, validator_balances=balances, # Take an entry out of the LRU.
activation_threshold=activation_threshold) # No copy is necessary, as we wrap the immutable backing with a new view.
if spec.fork == PHASE1: state = spec.BeaconState(backing=_custom_state_cache_dict[key])
# TODO: instead of upgrading a test phase0 genesis state we can also write a phase1 state helper. kw['state'] = state
# Decide based on performance/consistency results later.
state = phases[PHASE1].upgrade_to_phase1(state)
# Shard state slot must lag behind BeaconState slot by at least 1
# Will handle this more elegantly with fork mechanics
spec.process_slots(state, state.slot + 1)
kw['state'] = state
except KeyError:
raise TypeError('Spec decorator must come within state decorator to inject spec into state.')
return fn(*args, spec=spec, phases=phases, **kw) return fn(*args, spec=spec, phases=phases, **kw)
return entry return entry
return deco return deco

View File

@ -6,6 +6,7 @@ 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
from eth2spec.utils.ssz.ssz_typing import Bitlist from eth2spec.utils.ssz.ssz_typing import Bitlist
from lru import LRU
def run_attestation_processing(spec, state, attestation, valid=True): def run_attestation_processing(spec, state, attestation, valid=True):
@ -373,6 +374,27 @@ def prepare_state_with_attestations(spec, state, participation_fn=None):
return attestations return attestations
_prep_state_cache_dict = LRU(size=10)
def cached_prepare_state_with_attestations(spec, state):
"""
Cached version of prepare_state_with_attestations,
but does not return anything, and does not support a participation fn argument
"""
# If the pre-state is not already known in the LRU, then take it,
# prepare it with attestations, and put it in the LRU.
# The input state is likely already cached, so the hash-tree-root does not affect speed.
key = (spec.fork, state.hash_tree_root())
global _prep_state_cache_dict
if key not in _prep_state_cache_dict:
prepare_state_with_attestations(spec, state)
_prep_state_cache_dict[key] = state.get_backing() # cache the tree structure, not the view wrapping it.
# Put the LRU cache result into the state view, as if we transitioned the original view
state.set_backing(_prep_state_cache_dict[key])
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:

View File

@ -1,7 +1,7 @@
from random import Random from random import Random
from eth2spec.phase0 import spec as spec_phase0 from eth2spec.phase0 import spec as spec_phase0
from eth2spec.test.helpers.attestations import prepare_state_with_attestations from eth2spec.test.helpers.attestations import cached_prepare_state_with_attestations
from eth2spec.test.helpers.deposits import mock_deposit from eth2spec.test.helpers.deposits import mock_deposit
from eth2spec.test.helpers.state import next_epoch from eth2spec.test.helpers.state import next_epoch
from eth2spec.utils.ssz.ssz_typing import Container, uint64, List from eth2spec.utils.ssz.ssz_typing import Container, uint64, List
@ -25,17 +25,50 @@ def has_enough_for_reward(spec, state, index):
) )
def run_attestation_component_deltas(spec, state, component_delta_fn, matching_att_fn): def run_deltas(spec, state):
""" """
Run ``component_delta_fn``, yielding: Run all deltas functions yielding:
- pre-state ('pre') - pre-state ('pre')
- deltas ('deltas') - source deltas ('source_deltas')
- target deltas ('target_deltas')
- head deltas ('head_deltas')
- inclusion delay deltas ('inclusion_delay_deltas')
- inactivity penalty deltas ('inactivity_penalty_deltas')
""" """
yield 'pre', state yield 'pre', state
yield from run_attestation_component_deltas(
spec,
state,
spec.get_source_deltas,
spec.get_matching_source_attestations,
'source_deltas',
)
yield from run_attestation_component_deltas(
spec,
state,
spec.get_target_deltas,
spec.get_matching_target_attestations,
'target_deltas',
)
yield from run_attestation_component_deltas(
spec,
state,
spec.get_head_deltas,
spec.get_matching_head_attestations,
'head_deltas',
)
yield from run_get_inclusion_delay_deltas(spec, state)
yield from run_get_inactivity_penalty_deltas(spec, state)
def run_attestation_component_deltas(spec, state, component_delta_fn, matching_att_fn, deltas_name):
"""
Run ``component_delta_fn``, yielding:
- deltas ('{``deltas_name``}')
"""
rewards, penalties = component_delta_fn(state) rewards, penalties = component_delta_fn(state)
yield 'deltas', Deltas(rewards=rewards, penalties=penalties) yield deltas_name, Deltas(rewards=rewards, penalties=penalties)
matching_attestations = matching_att_fn(state, spec.get_previous_epoch(state)) matching_attestations = matching_att_fn(state, spec.get_previous_epoch(state))
matching_indices = spec.get_unslashed_attesting_indices(state, matching_attestations) matching_indices = spec.get_unslashed_attesting_indices(state, matching_attestations)
@ -62,10 +95,88 @@ def run_attestation_component_deltas(spec, state, component_delta_fn, matching_a
assert penalties[index] == 0 assert penalties[index] == 0
def run_get_inclusion_delay_deltas(spec, state):
"""
Run ``get_inclusion_delay_deltas``, yielding:
- inclusion delay deltas ('inclusion_delay_deltas')
"""
rewards, penalties = spec.get_inclusion_delay_deltas(state)
yield 'inclusion_delay_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
def run_get_inactivity_penalty_deltas(spec, state):
"""
Run ``get_inactivity_penalty_deltas``, yielding:
- inactivity penalty deltas ('inactivity_penalty_deltas')
"""
rewards, penalties = spec.get_inactivity_penalty_deltas(state)
yield 'inactivity_penalty_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 set_some_new_deposits(spec, state, rng): def set_some_new_deposits(spec, state, rng):
num_validators = len(state.validators) num_validators = len(state.validators)
# Set ~1/10 to just recently deposited # Set ~1/10 to just recently deposited
for index in range(num_validators): for index in range(num_validators):
# If not already active, skip
if not spec.is_active_validator(state.validators[index], spec.get_current_epoch(state)):
continue
if rng.randrange(num_validators) < num_validators // 10: if rng.randrange(num_validators) < num_validators // 10:
mock_deposit(spec, state, index) mock_deposit(spec, state, index)
# Set ~half of selected to eligible for activation # Set ~half of selected to eligible for activation
@ -102,87 +213,86 @@ def slash_random_validators(spec, state, rng):
spec.slash_validator(state, index) spec.slash_validator(state, index)
def run_test_empty(spec, state, runner): def run_test_empty(spec, state):
# Do not add any attestations to state # Do not add any attestations to state
yield from runner(spec, state) yield from run_deltas(spec, state)
def run_test_full_all_correct(spec, state, runner): def run_test_full_all_correct(spec, state):
prepare_state_with_attestations(spec, state) cached_prepare_state_with_attestations(spec, state)
yield from runner(spec, state) yield from run_deltas(spec, state)
def run_test_full_but_partial_participation(spec, state, runner, rng=Random(5522)): def run_test_full_but_partial_participation(spec, state, rng=Random(5522)):
prepare_state_with_attestations(spec, state) cached_prepare_state_with_attestations(spec, state)
for a in state.previous_epoch_attestations: for a in state.previous_epoch_attestations:
a.aggregation_bits = [rng.choice([True, False]) for _ in a.aggregation_bits] a.aggregation_bits = [rng.choice([True, False]) for _ in a.aggregation_bits]
yield from runner(spec, state) yield from run_deltas(spec, state)
def run_test_partial(spec, state, fraction_filled, runner): def run_test_partial(spec, state, fraction_filled):
prepare_state_with_attestations(spec, state) cached_prepare_state_with_attestations(spec, state)
# Remove portion of attestations # Remove portion of attestations
num_attestations = int(len(state.previous_epoch_attestations) * fraction_filled) num_attestations = int(len(state.previous_epoch_attestations) * fraction_filled)
state.previous_epoch_attestations = state.previous_epoch_attestations[:num_attestations] state.previous_epoch_attestations = state.previous_epoch_attestations[:num_attestations]
yield from runner(spec, state) yield from run_deltas(spec, state)
def run_test_half_full(spec, state, runner): def run_test_half_full(spec, state):
yield from run_test_partial(spec, state, 0.5, runner) yield from run_test_partial(spec, state, 0.5)
def run_test_one_attestation_one_correct(spec, state, runner): def run_test_one_attestation_one_correct(spec, state):
prepare_state_with_attestations(spec, state) cached_prepare_state_with_attestations(spec, state)
# Remove all attestations except for the first one # Remove all attestations except for the first one
state.previous_epoch_attestations = state.previous_epoch_attestations[:1] state.previous_epoch_attestations = state.previous_epoch_attestations[:1]
yield from runner(spec, state) yield from run_deltas(spec, state)
def run_test_with_not_yet_activated_validators(spec, state, runner, rng=Random(5555)): def run_test_with_not_yet_activated_validators(spec, state, rng=Random(5555)):
set_some_new_deposits(spec, state, rng) set_some_new_deposits(spec, state, rng)
prepare_state_with_attestations(spec, state) cached_prepare_state_with_attestations(spec, state)
yield from runner(spec, state) yield from run_deltas(spec, state)
def run_test_with_exited_validators(spec, state, runner, rng=Random(1337)): def run_test_with_exited_validators(spec, state, rng=Random(1337)):
exit_random_validators(spec, state, rng) exit_random_validators(spec, state, rng)
prepare_state_with_attestations(spec, state) cached_prepare_state_with_attestations(spec, state)
yield from runner(spec, state) yield from run_deltas(spec, state)
def run_test_with_slashed_validators(spec, state, runner, rng=Random(3322)): def run_test_with_slashed_validators(spec, state, rng=Random(3322)):
exit_random_validators(spec, state, rng) exit_random_validators(spec, state, rng)
slash_random_validators(spec, state, rng) slash_random_validators(spec, state, rng)
prepare_state_with_attestations(spec, state) cached_prepare_state_with_attestations(spec, state)
yield from runner(spec, state) yield from run_deltas(spec, state)
def run_test_some_very_low_effective_balances_that_attested(spec, state, runner): def run_test_some_very_low_effective_balances_that_attested(spec, state):
state.balances cached_prepare_state_with_attestations(spec, state)
prepare_state_with_attestations(spec, state)
# Set some balances to be very low (including 0) # Set some balances to be very low (including 0)
assert len(state.validators) >= 5 assert len(state.validators) >= 5
for i, index in enumerate(range(5)): for i, index in enumerate(range(5)):
state.validators[index].effective_balance = i state.validators[index].effective_balance = i
yield from runner(spec, state) yield from run_deltas(spec, state)
def run_test_some_very_low_effective_balances_that_did_not_attest(spec, state, runner): def run_test_some_very_low_effective_balances_that_did_not_attest(spec, state):
prepare_state_with_attestations(spec, state) cached_prepare_state_with_attestations(spec, state)
# Remove attestation # Remove attestation
attestation = state.previous_epoch_attestations[0] attestation = state.previous_epoch_attestations[0]
@ -192,11 +302,11 @@ def run_test_some_very_low_effective_balances_that_did_not_attest(spec, state, r
for i, index in enumerate(indices): for i, index in enumerate(indices):
state.validators[index].effective_balance = i state.validators[index].effective_balance = i
yield from runner(spec, state) yield from run_deltas(spec, state)
def run_test_full_fraction_incorrect(spec, state, correct_target, correct_head, fraction_incorrect, runner): def run_test_full_fraction_incorrect(spec, state, correct_target, correct_head, fraction_incorrect):
prepare_state_with_attestations(spec, state) cached_prepare_state_with_attestations(spec, state)
# Make fraction_incorrect of pending attestations have bad target/head as specified # Make fraction_incorrect of pending attestations have bad target/head as specified
num_incorrect = int(fraction_incorrect * len(state.previous_epoch_attestations)) num_incorrect = int(fraction_incorrect * len(state.previous_epoch_attestations))
@ -206,15 +316,97 @@ def run_test_full_fraction_incorrect(spec, state, correct_target, correct_head,
if not correct_head: if not correct_head:
pending_attestation.data.beacon_block_root = b'\x66' * 32 pending_attestation.data.beacon_block_root = b'\x66' * 32
yield from runner(spec, state) yield from run_deltas(spec, state)
def run_test_full_random(spec, state, runner, rng=Random(8020)): def run_test_full_delay_one_slot(spec, state):
cached_prepare_state_with_attestations(spec, state)
for a in state.previous_epoch_attestations:
a.inclusion_delay += 1
yield from run_deltas(spec, state)
def run_test_full_delay_max_slots(spec, state):
cached_prepare_state_with_attestations(spec, state)
for a in state.previous_epoch_attestations:
a.inclusion_delay += spec.SLOTS_PER_EPOCH
yield from run_deltas(spec, state)
def run_test_full_mixed_delay(spec, state, rng=Random(1234)):
cached_prepare_state_with_attestations(spec, state)
for a in state.previous_epoch_attestations:
a.inclusion_delay = rng.randint(1, spec.SLOTS_PER_EPOCH)
yield from run_deltas(spec, state)
def run_test_proposer_not_in_attestations(spec, state):
cached_prepare_state_with_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_deltas(spec, state)
def run_test_duplicate_attestations_at_later_slots(spec, state):
cached_prepare_state_with_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_deltas(spec, state)
def run_test_all_balances_too_low_for_reward(spec, state):
cached_prepare_state_with_attestations(spec, state)
for index in range(len(state.validators)):
state.validators[index].effective_balance = 10
yield from run_deltas(spec, state)
def run_test_full_random(spec, state, rng=Random(8020)):
set_some_new_deposits(spec, state, rng) set_some_new_deposits(spec, state, rng)
exit_random_validators(spec, state, rng) exit_random_validators(spec, state, rng)
slash_random_validators(spec, state, rng) slash_random_validators(spec, state, rng)
prepare_state_with_attestations(spec, state) cached_prepare_state_with_attestations(spec, state)
for pending_attestation in state.previous_epoch_attestations: for pending_attestation in state.previous_epoch_attestations:
# ~1/3 have bad target # ~1/3 have bad target
@ -228,4 +420,4 @@ def run_test_full_random(spec, state, runner, rng=Random(8020)):
# Random inclusion delay # Random inclusion delay
pending_attestation.inclusion_delay = rng.randint(1, spec.SLOTS_PER_EPOCH) pending_attestation.inclusion_delay = rng.randint(1, spec.SLOTS_PER_EPOCH)
yield from runner(spec, state) yield from run_deltas(spec, state)

View File

@ -1,89 +1,71 @@
from eth2spec.test.context import with_all_phases, spec_state_test 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 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 @with_all_phases
@spec_state_test @spec_state_test
def test_empty(spec, state): def test_empty(spec, state):
yield from rewards_helpers.run_test_empty(spec, state, run_get_source_deltas) yield from rewards_helpers.run_test_empty(spec, state)
@with_all_phases @with_all_phases
@spec_state_test @spec_state_test
def test_full_all_correct(spec, state): def test_full_all_correct(spec, state):
yield from rewards_helpers.run_test_full_all_correct(spec, state, run_get_source_deltas) yield from rewards_helpers.run_test_full_all_correct(spec, state)
@with_all_phases @with_all_phases
@spec_state_test @spec_state_test
def test_half_full(spec, state): def test_half_full(spec, state):
yield from rewards_helpers.run_test_half_full(spec, state, run_get_source_deltas) yield from rewards_helpers.run_test_half_full(spec, state)
@with_all_phases
@spec_state_test
def test_quarter_full(spec, state):
yield from rewards_helpers.run_test_partial(spec, state, 0.25)
@with_all_phases @with_all_phases
@spec_state_test @spec_state_test
def test_full_but_partial_participation(spec, state): def test_full_but_partial_participation(spec, state):
yield from rewards_helpers.run_test_full_but_partial_participation(spec, state, run_get_source_deltas) yield from rewards_helpers.run_test_full_but_partial_participation(spec, state)
@with_all_phases @with_all_phases
@spec_state_test @spec_state_test
def test_one_attestation_one_correct(spec, state): def test_one_attestation_one_correct(spec, state):
yield from rewards_helpers.run_test_one_attestation_one_correct(spec, state, run_get_source_deltas) yield from rewards_helpers.run_test_one_attestation_one_correct(spec, state)
@with_all_phases @with_all_phases
@spec_state_test @spec_state_test
def test_with_not_yet_activated_validators(spec, state): def test_with_not_yet_activated_validators(spec, state):
yield from rewards_helpers.run_test_with_not_yet_activated_validators(spec, state, run_get_source_deltas) yield from rewards_helpers.run_test_with_not_yet_activated_validators(spec, state)
@with_all_phases @with_all_phases
@spec_state_test @spec_state_test
def test_with_exited_validators(spec, state): def test_with_exited_validators(spec, state):
yield from rewards_helpers.run_test_with_exited_validators(spec, state, run_get_source_deltas) yield from rewards_helpers.run_test_with_exited_validators(spec, state)
@with_all_phases @with_all_phases
@spec_state_test @spec_state_test
def test_with_slashed_validators(spec, state): def test_with_slashed_validators(spec, state):
yield from rewards_helpers.run_test_with_slashed_validators(spec, state, run_get_source_deltas) yield from rewards_helpers.run_test_with_slashed_validators(spec, state)
@with_all_phases @with_all_phases
@spec_state_test @spec_state_test
def test_some_very_low_effective_balances_that_attested(spec, state): def test_some_very_low_effective_balances_that_attested(spec, state):
yield from rewards_helpers.run_test_some_very_low_effective_balances_that_attested( yield from rewards_helpers.run_test_some_very_low_effective_balances_that_attested(spec, state)
spec,
state,
run_get_source_deltas
)
@with_all_phases @with_all_phases
@spec_state_test @spec_state_test
def test_some_very_low_effective_balances_that_did_not_attest(spec, state): 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( yield from rewards_helpers.run_test_some_very_low_effective_balances_that_did_not_attest(spec, state)
spec,
state,
run_get_source_deltas,
)
# #
@ -101,7 +83,6 @@ def test_full_half_correct_target_incorrect_head(spec, state):
correct_target=True, correct_target=True,
correct_head=False, correct_head=False,
fraction_incorrect=0.5, fraction_incorrect=0.5,
runner=run_get_source_deltas
) )
@ -113,7 +94,6 @@ def test_full_correct_target_incorrect_head(spec, state):
correct_target=True, correct_target=True,
correct_head=False, correct_head=False,
fraction_incorrect=1.0, fraction_incorrect=1.0,
runner=run_get_source_deltas
) )
@ -125,7 +105,6 @@ def test_full_half_incorrect_target_incorrect_head(spec, state):
correct_target=False, correct_target=False,
correct_head=False, correct_head=False,
fraction_incorrect=0.5, fraction_incorrect=0.5,
runner=run_get_source_deltas
) )
@ -137,11 +116,40 @@ def test_full_half_incorrect_target_correct_head(spec, state):
correct_target=False, correct_target=False,
correct_head=True, correct_head=True,
fraction_incorrect=0.5, fraction_incorrect=0.5,
runner=run_get_source_deltas
) )
@with_all_phases @with_all_phases
@spec_state_test @spec_state_test
def test_full_random(spec, state): def test_full_delay_one_slot(spec, state):
yield from rewards_helpers.run_test_full_random(spec, state, run_get_source_deltas) yield from rewards_helpers.run_test_full_delay_one_slot(spec, state)
@with_all_phases
@spec_state_test
def test_full_delay_max_slots(spec, state):
yield from rewards_helpers.run_test_full_delay_max_slots(spec, state)
@with_all_phases
@spec_state_test
def test_full_mixed_delay(spec, state):
yield from rewards_helpers.run_test_full_mixed_delay(spec, state)
@with_all_phases
@spec_state_test
def test_proposer_not_in_attestations(spec, state):
yield from rewards_helpers.run_test_proposer_not_in_attestations(spec, state)
@with_all_phases
@spec_state_test
def test_duplicate_attestations_at_later_slots(spec, state):
yield from rewards_helpers.run_test_duplicate_attestations_at_later_slots(spec, state)
@with_all_phases
@spec_state_test
def test_all_balances_too_low_for_reward(spec, state):
yield from rewards_helpers.run_test_all_balances_too_low_for_reward(spec, state)

View File

@ -1,136 +0,0 @@
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_not_yet_activated_validators(spec, state):
yield from rewards_helpers.run_test_with_not_yet_activated_validators(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)

View File

@ -1,231 +0,0 @@
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_not_yet_activated_validators_no_leak(spec, state):
yield from rewards_helpers.run_test_with_not_yet_activated_validators(
spec,
state,
run_get_inactivity_penalty_deltas,
)
@with_all_phases
@spec_state_test
def test_with_not_yet_activated_validators_leak(spec, state):
transition_state_to_leak(spec, state)
yield from rewards_helpers.run_test_with_not_yet_activated_validators(
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)

View File

@ -1,213 +0,0 @@
from random import Random
from eth2spec.test.context import with_all_phases, spec_state_test
from eth2spec.test.helpers.attestations import prepare_state_with_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_not_yet_activated_validators(spec, state):
yield from rewards_helpers.run_test_with_not_yet_activated_validators(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_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_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_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_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_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_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)

View File

@ -1,140 +0,0 @@
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_not_yet_activated_validators(spec, state):
yield from rewards_helpers.run_test_with_not_yet_activated_validators(spec, state, run_get_target_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_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)

View File

@ -0,0 +1,190 @@
from eth2spec.test.context import with_all_phases, spec_state_test
from eth2spec.test.helpers.state import next_epoch
import eth2spec.test.helpers.rewards as rewards_helpers
from lru import LRU
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)
_cache_dict = LRU(size=10)
def leaking(epochs=None):
def deco(fn):
def entry(*args, spec, state, **kw):
# If the pre-state is not already known in the LRU, then take it,
# transition it to leak, and put it in the LRU.
# The input state is likely already cached, so the hash-tree-root does not affect speed.
key = (state.hash_tree_root(), spec.MIN_EPOCHS_TO_INACTIVITY_PENALTY, spec.SLOTS_PER_EPOCH, epochs)
global _cache_dict
if key not in _cache_dict:
transition_state_to_leak(spec, state, epochs=epochs)
_cache_dict[key] = state.get_backing() # cache the tree structure, not the view wrapping it.
# Take an entry out of the LRU.
# No copy is necessary, as we wrap the immutable backing with a new view.
state = spec.BeaconState(backing=_cache_dict[key])
return fn(*args, spec=spec, state=state, **kw)
return entry
return deco
@with_all_phases
@spec_state_test
@leaking()
def test_empty_leak(spec, state):
yield from rewards_helpers.run_test_empty(spec, state)
@with_all_phases
@spec_state_test
@leaking()
def test_full_leak(spec, state):
yield from rewards_helpers.run_test_full_all_correct(spec, state)
@with_all_phases
@spec_state_test
@leaking()
def test_half_full_leak(spec, state):
yield from rewards_helpers.run_test_half_full(spec, state)
@with_all_phases
@spec_state_test
@leaking()
def test_quarter_full_leak(spec, state):
yield from rewards_helpers.run_test_partial(spec, state, 0.25)
@with_all_phases
@spec_state_test
@leaking()
def test_full_but_partial_participation_leak(spec, state):
yield from rewards_helpers.run_test_full_but_partial_participation(spec, state)
@with_all_phases
@spec_state_test
@leaking()
def test_one_attestation_one_correct_leak(spec, state):
yield from rewards_helpers.run_test_one_attestation_one_correct(spec, state)
@with_all_phases
@spec_state_test
@leaking()
def test_with_not_yet_activated_validators_leak(spec, state):
yield from rewards_helpers.run_test_with_not_yet_activated_validators(spec, state)
@with_all_phases
@spec_state_test
@leaking()
def test_with_exited_validators_leak(spec, state):
yield from rewards_helpers.run_test_with_exited_validators(spec, state)
@with_all_phases
@spec_state_test
@leaking()
def test_with_slashed_validators_leak(spec, state):
yield from rewards_helpers.run_test_with_slashed_validators(spec, state)
@with_all_phases
@spec_state_test
@leaking()
def test_some_very_low_effective_balances_that_attested_leak(spec, state):
yield from rewards_helpers.run_test_some_very_low_effective_balances_that_attested(spec, state)
@with_all_phases
@spec_state_test
@leaking()
def test_some_very_low_effective_balances_that_did_not_attest_leak(spec, state):
yield from rewards_helpers.run_test_some_very_low_effective_balances_that_did_not_attest(spec, state)
#
# 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
@leaking()
def test_full_half_correct_target_incorrect_head_leak(spec, state):
yield from rewards_helpers.run_test_full_fraction_incorrect(
spec, state,
correct_target=True,
correct_head=False,
fraction_incorrect=0.5,
)
@with_all_phases
@spec_state_test
@leaking()
def test_full_correct_target_incorrect_head_leak(spec, state):
yield from rewards_helpers.run_test_full_fraction_incorrect(
spec, state,
correct_target=True,
correct_head=False,
fraction_incorrect=1.0,
)
@with_all_phases
@spec_state_test
@leaking()
def test_full_half_incorrect_target_incorrect_head_leak(spec, state):
yield from rewards_helpers.run_test_full_fraction_incorrect(
spec, state,
correct_target=False,
correct_head=False,
fraction_incorrect=0.5,
)
@with_all_phases
@spec_state_test
@leaking()
def test_full_half_incorrect_target_correct_head_leak(spec, state):
yield from rewards_helpers.run_test_full_fraction_incorrect(
spec, state,
correct_target=False,
correct_head=True,
fraction_incorrect=0.5,
)
@with_all_phases
@spec_state_test
@leaking()
def test_full_random_leak(spec, state):
yield from rewards_helpers.run_test_full_random(spec, state)
@with_all_phases
@spec_state_test
@leaking(epochs=5)
def test_full_random_five_epoch_leak(spec, state):
yield from rewards_helpers.run_test_full_random(spec, state)
@with_all_phases
@spec_state_test
@leaking(epochs=10)
def test_full_random_ten_epoch_leak(spec, state):
yield from rewards_helpers.run_test_full_random(spec, state)

View File

@ -0,0 +1,45 @@
from random import Random
from eth2spec.test.context import (
with_all_phases,
spec_test,
spec_state_test,
with_custom_state,
single_phase,
low_balances, misc_balances,
)
import eth2spec.test.helpers.rewards as rewards_helpers
@with_all_phases
@spec_state_test
def test_full_random_0(spec, state):
yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(1010))
@with_all_phases
@spec_state_test
def test_full_random_1(spec, state):
yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(2020))
@with_all_phases
@spec_state_test
def test_full_random_2(spec, state):
yield from rewards_helpers.run_test_full_random(spec, state, rng=Random(3030))
@with_all_phases
@with_custom_state(balances_fn=low_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
@spec_test
@single_phase
def test_full_random_low_balances(spec, state):
yield from rewards_helpers.run_test_full_random(spec, state)
@with_all_phases
@with_custom_state(balances_fn=misc_balances, threshold_fn=lambda spec: spec.EJECTION_BALANCE)
@spec_test
@single_phase
def test_full_random_misc_balances(spec, state):
yield from rewards_helpers.run_test_full_random(spec, state)

View File

@ -38,7 +38,7 @@ The provided pre-state is already transitioned to just before the specific sub-t
Sub-transitions: Sub-transitions:
- `justification_and_finalization` - `justification_and_finalization`
- *`rewards_and_penalties` - planned testing extension* - `rewards_and_penalties` (limited to `minimal` config)
- `registry_updates` - `registry_updates`
- `slashings` - `slashings`
- `final_updates` - `final_updates`

View File

@ -1,8 +1,15 @@
# Rewards tests # Rewards tests
The different rewards deltas sub-functions are testing individually with the test handlers, each returning the related `rewards`/`penalties`. All rewards deltas sub-functions are tested for each test case.
There is no "change" factor, the rewards/penalties outputs are pure functions with just the pre-state as input. 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.) (See test condition documentation on how to run the tests.)
`Deltas` is defined as:
```python
class Deltas(Container):
rewards: List[Gwei, VALIDATOR_REGISTRY_LIMIT]
penalties: List[Gwei, VALIDATOR_REGISTRY_LIMIT]
```
## Test case format ## Test case format
@ -22,31 +29,47 @@ A YAML-encoded `BeaconState`, the state before running the rewards sub-function.
Also available as `pre.ssz`. Also available as `pre.ssz`.
### `deltas.yaml` ### `source_deltas.yaml`
A YAML-encoded `Deltas` representing the rewards and penalties returned by the rewards sub-function A YAML-encoded `Deltas` representing the rewards and penalties returned by the rewards the `get_source_deltas` function
Where `Deltas` is defined as: Also available as `source_deltas.ssz`.
```python
class Deltas(Container):
rewards: List[uint64, VALIDATOR_REGISTRY_LIMIT]
penalties: List[uint64, VALIDATOR_REGISTRY_LIMIT]
```
Also available as `deltas.ssz`. ### `target_deltas.yaml`
A YAML-encoded `Deltas` representing the rewards and penalties returned by the rewards the `get_target_deltas` function
Also available as `target_deltas.ssz`.
### `head_deltas.yaml`
A YAML-encoded `Deltas` representing the rewards and penalties returned by the rewards the `get_head_deltas` function
Also available as `head_deltas.ssz`.
### `inclusion_delay_deltas.yaml`
A YAML-encoded `Deltas` representing the rewards and penalties returned by the rewards the `get_inclusion_delay_deltas` function
Also available as `inclusion_delay_deltas.ssz`.
### `inactivity_penalty_deltas.yaml`
A YAML-encoded `Deltas` representing the rewards and penalties returned by the rewards the `get_inactivity_penalty_deltas` function
Also available as `inactivity_penalty_deltas.ssz`.
## Condition ## Condition
A handler of the `rewards` test-runner should process these cases, A handler of the `rewards` test-runner should process these cases,
calling the corresponding rewards deltas function (same name in spec). calling the corresponding rewards deltas function for each set of deltas.
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 pre-state is ready to be input into each rewards deltas function.
The provided `deltas` should match the return values of the The provided `deltas` should match the return values of the
handler. Specifically the following must hold true: deltas function. Specifically the following must hold true for each set of deltas:
```python ```python
deltas.rewards == handler(state)[0] deltas.rewards == deltas_function(state)[0]
deltas.penalties == handler(state)[1] deltas.penalties == deltas_function(state)[1]
``` ```

View File

@ -3,11 +3,9 @@ from typing import Iterable
from eth2spec.phase0 import spec as spec_phase0 from eth2spec.phase0 import spec as spec_phase0
from eth2spec.phase1 import spec as spec_phase1 from eth2spec.phase1 import spec as spec_phase1
from eth2spec.test.phase_0.rewards import ( from eth2spec.test.phase_0.rewards import (
test_get_source_deltas, test_basic,
test_get_target_deltas, test_leak,
test_get_head_deltas, test_random,
test_get_inclusion_delay_deltas,
test_get_inactivity_penalty_deltas,
) )
from gen_base import gen_runner, gen_typing from gen_base import gen_runner, gen_typing
from gen_from_tests.gen import generate_from_tests from gen_from_tests.gen import generate_from_tests
@ -16,7 +14,7 @@ from eth2spec.config import config_util
from eth2spec.test.context import PHASE0 from eth2spec.test.context import PHASE0
def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typing.TestProvider: def create_provider(tests_src, config_name: str) -> gen_typing.TestProvider:
def prepare_fn(configs_path: str) -> str: def prepare_fn(configs_path: str) -> str:
config_util.prepare_config(configs_path, config_name) config_util.prepare_config(configs_path, config_name)
@ -27,7 +25,7 @@ def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typin
def cases_fn() -> Iterable[gen_typing.TestCase]: def cases_fn() -> Iterable[gen_typing.TestCase]:
return generate_from_tests( return generate_from_tests(
runner_name='rewards', runner_name='rewards',
handler_name=handler_name, handler_name='core',
src=tests_src, src=tests_src,
fork_name=PHASE0, fork_name=PHASE0,
) )
@ -36,15 +34,11 @@ def create_provider(handler_name: str, tests_src, config_name: str) -> gen_typin
if __name__ == "__main__": if __name__ == "__main__":
gen_runner.run_generator("epoch_processing", [ gen_runner.run_generator("rewards", [
create_provider('get_source_deltas', test_get_source_deltas, 'minimal'), create_provider(test_basic, 'minimal'),
create_provider('get_source_deltas', test_get_source_deltas, 'mainnet'), create_provider(test_basic, 'mainnet'),
create_provider('get_target_deltas', test_get_target_deltas, 'minimal'), create_provider(test_leak, 'minimal'),
create_provider('get_target_deltas', test_get_target_deltas, 'mainnet'), create_provider(test_leak, 'mainnet'),
create_provider('get_head_deltas', test_get_head_deltas, 'minimal'), create_provider(test_random, 'minimal'),
create_provider('get_head_deltas', test_get_head_deltas, 'mainnet'), create_provider(test_random, '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'),
]) ])