add test_get_inclusion_delay_deltas
This commit is contained in:
parent
a7354bd358
commit
5194c1f2d2
|
@ -1,3 +1,5 @@
|
||||||
|
from random import Random
|
||||||
|
|
||||||
from eth2spec.test.helpers.attestations import prepare_state_with_full_attestations
|
from eth2spec.test.helpers.attestations import prepare_state_with_full_attestations
|
||||||
from eth2spec.utils.ssz.ssz_typing import Container, uint64, List
|
from eth2spec.utils.ssz.ssz_typing import Container, uint64, List
|
||||||
|
|
||||||
|
@ -7,6 +9,13 @@ class Deltas(Container):
|
||||||
delta_list: List[uint64, 2**30]
|
delta_list: List[uint64, 2**30]
|
||||||
|
|
||||||
|
|
||||||
|
def has_enough_for_reward(spec, state, index):
|
||||||
|
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):
|
def run_attestation_component_deltas(spec, state, component_delta_fn, matching_att_fn):
|
||||||
"""
|
"""
|
||||||
Run ``component_delta_fn``, yielding:
|
Run ``component_delta_fn``, yielding:
|
||||||
|
@ -25,11 +34,7 @@ def run_attestation_component_deltas(spec, state, component_delta_fn, matching_a
|
||||||
matching_indices = spec.get_unslashed_attesting_indices(state, matching_attestations)
|
matching_indices = spec.get_unslashed_attesting_indices(state, matching_attestations)
|
||||||
for index in spec.get_eligible_validator_indices(state):
|
for index in spec.get_eligible_validator_indices(state):
|
||||||
validator = state.validators[index]
|
validator = state.validators[index]
|
||||||
enough_for_reward = (
|
enough_for_reward = has_enough_for_reward(spec, state, index)
|
||||||
validator.effective_balance * spec.BASE_REWARD_FACTOR
|
|
||||||
> spec.integer_squareroot(spec.get_total_active_balance(state)) // spec.BASE_REWARDS_PER_EPOCH
|
|
||||||
)
|
|
||||||
|
|
||||||
if index in matching_indices and not validator.slashed:
|
if index in matching_indices and not validator.slashed:
|
||||||
if enough_for_reward:
|
if enough_for_reward:
|
||||||
assert rewards[index] > 0
|
assert rewards[index] > 0
|
||||||
|
@ -56,15 +61,29 @@ def test_full_all_correct(spec, state, runner):
|
||||||
yield from runner(spec, state)
|
yield from runner(spec, state)
|
||||||
|
|
||||||
|
|
||||||
def test_half_full(spec, state, runner):
|
def test_full_but_partial_participation(spec, state, runner, rng=Random(5522)):
|
||||||
prepare_state_with_full_attestations(spec, state)
|
prepare_state_with_full_attestations(spec, state)
|
||||||
|
|
||||||
# Remove half of attestations
|
for a in state.previous_epoch_attestations:
|
||||||
state.previous_epoch_attestations = state.previous_epoch_attestations[:len(state.previous_epoch_attestations) // 2]
|
a.aggregation_bits = [rng.choice([True, False]) for _ in a.aggregation_bits]
|
||||||
|
|
||||||
yield from runner(spec, state)
|
yield from runner(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
def 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 test_half_full(spec, state, runner):
|
||||||
|
yield from test_partial(spec, state, 0.5, runner)
|
||||||
|
|
||||||
|
|
||||||
def test_one_attestation_one_correct(spec, state, runner):
|
def test_one_attestation_one_correct(spec, state, runner):
|
||||||
prepare_state_with_full_attestations(spec, state)
|
prepare_state_with_full_attestations(spec, state)
|
||||||
|
|
||||||
|
@ -84,12 +103,16 @@ def test_with_slashed_validators(spec, state, runner):
|
||||||
yield from runner(spec, state)
|
yield from runner(spec, state)
|
||||||
|
|
||||||
|
|
||||||
def test_some_zero_effective_balances_that_attested(spec, state, runner):
|
def test_some_very_low_effective_balances_that_attested(spec, state, runner):
|
||||||
|
state.balances
|
||||||
prepare_state_with_full_attestations(spec, state)
|
prepare_state_with_full_attestations(spec, state)
|
||||||
|
|
||||||
# Set some balances to zero
|
# Set some balances to be very low (including 0)
|
||||||
state.validators[0].effective_balance = 0
|
state.validators[0].effective_balance = 0
|
||||||
state.validators[1].effective_balance = 0
|
state.validators[1].effective_balance = 2
|
||||||
|
state.validators[2].effective_balance = 10
|
||||||
|
state.validators[3].effective_balance = 100
|
||||||
|
state.validators[4].effective_balance = 1000
|
||||||
|
|
||||||
yield from runner(spec, state)
|
yield from runner(spec, state)
|
||||||
|
|
||||||
|
@ -97,9 +120,8 @@ def test_some_zero_effective_balances_that_attested(spec, state, runner):
|
||||||
def test_some_zero_effective_balances_that_did_not_attest(spec, state, runner):
|
def test_some_zero_effective_balances_that_did_not_attest(spec, state, runner):
|
||||||
prepare_state_with_full_attestations(spec, state)
|
prepare_state_with_full_attestations(spec, state)
|
||||||
|
|
||||||
# Set some balances to zero
|
|
||||||
attestation = state.previous_epoch_attestations[0]
|
|
||||||
# Remove attestation
|
# Remove attestation
|
||||||
|
attestation = state.previous_epoch_attestations[0]
|
||||||
state.previous_epoch_attestations = state.previous_epoch_attestations[1:]
|
state.previous_epoch_attestations = state.previous_epoch_attestations[1:]
|
||||||
# Set removed indices effective balance to zero
|
# Set removed indices effective balance to zero
|
||||||
indices = spec.get_unslashed_attesting_indices(state, [attestation])
|
indices = spec.get_unslashed_attesting_indices(state, [attestation])
|
||||||
|
@ -121,3 +143,21 @@ def test_full_fraction_incorrect(spec, state, correct_target, correct_head, frac
|
||||||
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 runner(spec, state)
|
||||||
|
|
||||||
|
|
||||||
|
def test_full_random(spec, state, runner, rng=Random(8020)):
|
||||||
|
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)
|
||||||
|
|
|
@ -51,8 +51,8 @@ def test_with_slashed_validators(spec, state):
|
||||||
|
|
||||||
@with_all_phases
|
@with_all_phases
|
||||||
@spec_state_test
|
@spec_state_test
|
||||||
def test_some_zero_effective_balances_that_attested(spec, state):
|
def test_some_very_low_effective_balances_that_attested(spec, state):
|
||||||
yield from rewards_helpers.test_some_zero_effective_balances_that_attested(spec, state, run_get_head_deltas)
|
yield from rewards_helpers.test_some_very_low_effective_balances_that_attested(spec, state, run_get_head_deltas)
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases
|
@with_all_phases
|
||||||
|
@ -107,3 +107,9 @@ def test_full_half_incorrect_target_correct_head(spec, state):
|
||||||
fraction_incorrect=0.5,
|
fraction_incorrect=0.5,
|
||||||
runner=run_get_head_deltas
|
runner=run_get_head_deltas
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_random(spec, state):
|
||||||
|
yield from rewards_helpers.test_full_random(spec, state, run_get_head_deltas)
|
||||||
|
|
|
@ -0,0 +1,208 @@
|
||||||
|
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 has_enough_for_reward
|
||||||
|
import eth2spec.test.helpers.rewards as rewards_helpers
|
||||||
|
from eth2spec.utils.ssz.ssz_typing import Container, uint64, List
|
||||||
|
|
||||||
|
|
||||||
|
# HACK to get the generators outputting correctly
|
||||||
|
class Deltas(Container):
|
||||||
|
delta_list: List[uint64, 2**30]
|
||||||
|
|
||||||
|
|
||||||
|
def run_get_inclusion_delay_deltas(spec, state):
|
||||||
|
"""
|
||||||
|
Run ``get_inclusion_delay_deltas``, yielding:
|
||||||
|
- pre-state ('pre')
|
||||||
|
- rewards ('rewards')
|
||||||
|
- penalties ('penalties')
|
||||||
|
"""
|
||||||
|
|
||||||
|
yield 'pre', state
|
||||||
|
|
||||||
|
rewards, penalties = spec.get_inclusion_delay_deltas(state)
|
||||||
|
|
||||||
|
yield 'rewards', Deltas(delta_list=rewards)
|
||||||
|
yield 'penalties', Deltas(delta_list=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.test_empty(spec, state, run_get_inclusion_delay_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full(spec, state):
|
||||||
|
yield from rewards_helpers.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.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.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.test_full_but_partial_participation(spec, state, run_get_inclusion_delay_deltas)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_with_slashed_validators(spec, state):
|
||||||
|
yield from rewards_helpers.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.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.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:
|
||||||
|
# Do not create later duplicate if goes into next epoch
|
||||||
|
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)
|
|
@ -37,6 +37,12 @@ def test_half_full(spec, state):
|
||||||
yield from rewards_helpers.test_half_full(spec, state, run_get_source_deltas)
|
yield from rewards_helpers.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.test_full_but_partial_participation(spec, state, run_get_source_deltas)
|
||||||
|
|
||||||
|
|
||||||
@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):
|
||||||
|
@ -51,8 +57,8 @@ def test_with_slashed_validators(spec, state):
|
||||||
|
|
||||||
@with_all_phases
|
@with_all_phases
|
||||||
@spec_state_test
|
@spec_state_test
|
||||||
def test_some_zero_effective_balances_that_attested(spec, state):
|
def test_some_very_low_effective_balances_that_attested(spec, state):
|
||||||
yield from rewards_helpers.test_some_zero_effective_balances_that_attested(spec, state, run_get_source_deltas)
|
yield from rewards_helpers.test_some_very_low_effective_balances_that_attested(spec, state, run_get_source_deltas)
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases
|
@with_all_phases
|
||||||
|
@ -114,3 +120,9 @@ def test_full_half_incorrect_target_correct_head(spec, state):
|
||||||
fraction_incorrect=0.5,
|
fraction_incorrect=0.5,
|
||||||
runner=run_get_source_deltas
|
runner=run_get_source_deltas
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_random(spec, state):
|
||||||
|
yield from rewards_helpers.test_full_random(spec, state, run_get_source_deltas)
|
||||||
|
|
|
@ -37,6 +37,12 @@ def test_half_full(spec, state):
|
||||||
yield from rewards_helpers.test_half_full(spec, state, run_get_target_deltas)
|
yield from rewards_helpers.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.test_full_but_partial_participation(spec, state, run_get_target_deltas)
|
||||||
|
|
||||||
|
|
||||||
@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):
|
||||||
|
@ -51,8 +57,8 @@ def test_with_slashed_validators(spec, state):
|
||||||
|
|
||||||
@with_all_phases
|
@with_all_phases
|
||||||
@spec_state_test
|
@spec_state_test
|
||||||
def test_some_zero_effective_balances_that_attested(spec, state):
|
def test_some_very_low_effective_balances_that_attested(spec, state):
|
||||||
yield from rewards_helpers.test_some_zero_effective_balances_that_attested(spec, state, run_get_target_deltas)
|
yield from rewards_helpers.test_some_very_low_effective_balances_that_attested(spec, state, run_get_target_deltas)
|
||||||
|
|
||||||
|
|
||||||
@with_all_phases
|
@with_all_phases
|
||||||
|
@ -107,3 +113,9 @@ def test_full_half_incorrect_target_correct_head(spec, state):
|
||||||
fraction_incorrect=0.5,
|
fraction_incorrect=0.5,
|
||||||
runner=run_get_target_deltas
|
runner=run_get_target_deltas
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@with_all_phases
|
||||||
|
@spec_state_test
|
||||||
|
def test_full_random(spec, state):
|
||||||
|
yield from rewards_helpers.test_full_random(spec, state, run_get_target_deltas)
|
||||||
|
|
Loading…
Reference in New Issue