Merge pull request #2570 from ralexstokes/fix-deposits-randomized-tests
Fix randomized deposit testing
This commit is contained in:
commit
125bf22494
|
@ -113,8 +113,12 @@ def get_random_attestations(spec, state, rng):
|
||||||
return attestations
|
return attestations
|
||||||
|
|
||||||
|
|
||||||
def prepare_state_and_get_random_deposits(spec, state, rng):
|
def get_random_deposits(spec, state, rng, num_deposits=None):
|
||||||
num_deposits = rng.randrange(1, spec.MAX_DEPOSITS)
|
if not num_deposits:
|
||||||
|
num_deposits = rng.randrange(1, spec.MAX_DEPOSITS)
|
||||||
|
|
||||||
|
if num_deposits == 0:
|
||||||
|
return [], b"\x00" * 32
|
||||||
|
|
||||||
deposit_data_leaves = [spec.DepositData() for _ in range(len(state.validators))]
|
deposit_data_leaves = [spec.DepositData() for _ in range(len(state.validators))]
|
||||||
deposits = []
|
deposits = []
|
||||||
|
@ -132,15 +136,19 @@ def prepare_state_and_get_random_deposits(spec, state, rng):
|
||||||
signed=True,
|
signed=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
state.eth1_data.deposit_root = root
|
|
||||||
state.eth1_data.deposit_count += num_deposits
|
|
||||||
|
|
||||||
# Then for that context, build deposits/proofs
|
# Then for that context, build deposits/proofs
|
||||||
for i in range(num_deposits):
|
for i in range(num_deposits):
|
||||||
index = len(state.validators) + i
|
index = len(state.validators) + i
|
||||||
deposit, _, _ = deposit_from_context(spec, deposit_data_leaves, index)
|
deposit, _, _ = deposit_from_context(spec, deposit_data_leaves, index)
|
||||||
deposits.append(deposit)
|
deposits.append(deposit)
|
||||||
|
|
||||||
|
return deposits, root
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_state_and_get_random_deposits(spec, state, rng, num_deposits=None):
|
||||||
|
deposits, root = get_random_deposits(spec, state, rng, num_deposits=num_deposits)
|
||||||
|
state.eth1_data.deposit_root = root
|
||||||
|
state.eth1_data.deposit_count += len(deposits)
|
||||||
return deposits
|
return deposits
|
||||||
|
|
||||||
|
|
||||||
|
@ -192,10 +200,7 @@ def get_random_sync_aggregate(spec, state, slot, block_root=None, fraction_parti
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def build_random_block_from_state_for_next_slot(spec, state, rng=Random(2188)):
|
def build_random_block_from_state_for_next_slot(spec, state, rng=Random(2188), deposits=None):
|
||||||
# prepare state for deposits before building block
|
|
||||||
deposits = prepare_state_and_get_random_deposits(spec, state, rng)
|
|
||||||
|
|
||||||
block = build_empty_block_for_next_slot(spec, state)
|
block = build_empty_block_for_next_slot(spec, state)
|
||||||
proposer_slashings = get_random_proposer_slashings(spec, state, rng)
|
proposer_slashings = get_random_proposer_slashings(spec, state, rng)
|
||||||
block.body.proposer_slashings = proposer_slashings
|
block.body.proposer_slashings = proposer_slashings
|
||||||
|
@ -205,7 +210,8 @@ def build_random_block_from_state_for_next_slot(spec, state, rng=Random(2188)):
|
||||||
]
|
]
|
||||||
block.body.attester_slashings = get_random_attester_slashings(spec, state, rng, slashed_indices)
|
block.body.attester_slashings = get_random_attester_slashings(spec, state, rng, slashed_indices)
|
||||||
block.body.attestations = get_random_attestations(spec, state, rng)
|
block.body.attestations = get_random_attestations(spec, state, rng)
|
||||||
block.body.deposits = deposits
|
if deposits:
|
||||||
|
block.body.deposits = deposits
|
||||||
|
|
||||||
# cannot include to be slashed indices as exits
|
# cannot include to be slashed indices as exits
|
||||||
slashed_indices = set([
|
slashed_indices = set([
|
||||||
|
@ -224,7 +230,9 @@ def run_test_full_random_operations(spec, state, rng=Random(2080)):
|
||||||
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
|
# move state forward SHARD_COMMITTEE_PERIOD epochs to allow for exit
|
||||||
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
|
state.slot += spec.config.SHARD_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH
|
||||||
|
|
||||||
block = build_random_block_from_state_for_next_slot(spec, state, rng)
|
# prepare state for deposits before building block
|
||||||
|
deposits = prepare_state_and_get_random_deposits(spec, state, rng)
|
||||||
|
block = build_random_block_from_state_for_next_slot(spec, state, rng, deposits=deposits)
|
||||||
|
|
||||||
yield 'pre', state
|
yield 'pre', state
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ from typing import Callable
|
||||||
from eth2spec.test.helpers.multi_operations import (
|
from eth2spec.test.helpers.multi_operations import (
|
||||||
build_random_block_from_state_for_next_slot,
|
build_random_block_from_state_for_next_slot,
|
||||||
get_random_sync_aggregate,
|
get_random_sync_aggregate,
|
||||||
|
prepare_state_and_get_random_deposits,
|
||||||
)
|
)
|
||||||
from eth2spec.test.helpers.inactivity_scores import (
|
from eth2spec.test.helpers.inactivity_scores import (
|
||||||
randomize_inactivity_scores,
|
randomize_inactivity_scores,
|
||||||
|
@ -28,13 +29,35 @@ from eth2spec.test.helpers.state import (
|
||||||
# state
|
# state
|
||||||
|
|
||||||
|
|
||||||
def randomize_state(spec, state, exit_fraction=0.1, slash_fraction=0.1):
|
def _randomize_deposit_state(spec, state, stats):
|
||||||
|
"""
|
||||||
|
To introduce valid, randomized deposits, the ``state`` deposit sub-state
|
||||||
|
must be coordinated with the data that will ultimately go into blocks.
|
||||||
|
|
||||||
|
This function randomizes the ``state`` in a way that can signal downstream to
|
||||||
|
the block constructors how they should (or should not) make some randomized deposits.
|
||||||
|
"""
|
||||||
|
rng = Random(999)
|
||||||
|
block_count = stats.get("block_count", 0)
|
||||||
|
deposits = []
|
||||||
|
if block_count > 0:
|
||||||
|
num_deposits = rng.randrange(1, block_count * spec.MAX_DEPOSITS)
|
||||||
|
deposits = prepare_state_and_get_random_deposits(spec, state, rng, num_deposits=num_deposits)
|
||||||
|
return {
|
||||||
|
"deposits": deposits,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def randomize_state(spec, state, stats, exit_fraction=0.1, slash_fraction=0.1):
|
||||||
randomize_state_helper(spec, state, exit_fraction=exit_fraction, slash_fraction=slash_fraction)
|
randomize_state_helper(spec, state, exit_fraction=exit_fraction, slash_fraction=slash_fraction)
|
||||||
|
scenario_state = _randomize_deposit_state(spec, state, stats)
|
||||||
|
return scenario_state
|
||||||
|
|
||||||
|
|
||||||
def randomize_state_altair(spec, state):
|
def randomize_state_altair(spec, state, stats):
|
||||||
randomize_state(spec, state, exit_fraction=0.1, slash_fraction=0.1)
|
scenario_state = randomize_state(spec, state, stats, exit_fraction=0.1, slash_fraction=0.1)
|
||||||
randomize_inactivity_scores(spec, state)
|
randomize_inactivity_scores(spec, state)
|
||||||
|
return scenario_state
|
||||||
|
|
||||||
|
|
||||||
# epochs
|
# epochs
|
||||||
|
@ -67,7 +90,7 @@ def penultimate_slot_in_epoch(spec):
|
||||||
|
|
||||||
# blocks
|
# blocks
|
||||||
|
|
||||||
def no_block(_spec, _pre_state, _signed_blocks):
|
def no_block(_spec, _pre_state, _signed_blocks, _scenario_state):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,9 +100,10 @@ BLOCK_ATTEMPTS = 32
|
||||||
|
|
||||||
|
|
||||||
def _warn_if_empty_operations(block):
|
def _warn_if_empty_operations(block):
|
||||||
if len(block.body.deposits) == 0:
|
"""
|
||||||
warnings.warn(f"deposits missing in block at slot {block.slot}")
|
NOTE: a block may be missing deposits depending on how many were created
|
||||||
|
and already inserted into existing blocks in a given scenario.
|
||||||
|
"""
|
||||||
if len(block.body.proposer_slashings) == 0:
|
if len(block.body.proposer_slashings) == 0:
|
||||||
warnings.warn(f"proposer slashings missing in block at slot {block.slot}")
|
warnings.warn(f"proposer slashings missing in block at slot {block.slot}")
|
||||||
|
|
||||||
|
@ -93,7 +117,13 @@ def _warn_if_empty_operations(block):
|
||||||
warnings.warn(f"voluntary exits missing in block at slot {block.slot}")
|
warnings.warn(f"voluntary exits missing in block at slot {block.slot}")
|
||||||
|
|
||||||
|
|
||||||
def random_block(spec, state, _signed_blocks):
|
def _pull_deposits_from_scenario_state(spec, scenario_state, existing_block_count):
|
||||||
|
all_deposits = scenario_state.get("deposits", [])
|
||||||
|
start = existing_block_count * spec.MAX_DEPOSITS
|
||||||
|
return all_deposits[start:start + spec.MAX_DEPOSITS]
|
||||||
|
|
||||||
|
|
||||||
|
def random_block(spec, state, signed_blocks, scenario_state):
|
||||||
"""
|
"""
|
||||||
Produce a random block.
|
Produce a random block.
|
||||||
NOTE: this helper may mutate state, as it will attempt
|
NOTE: this helper may mutate state, as it will attempt
|
||||||
|
@ -118,7 +148,8 @@ def random_block(spec, state, _signed_blocks):
|
||||||
next_slot(spec, state)
|
next_slot(spec, state)
|
||||||
next_slot(spec, temp_state)
|
next_slot(spec, temp_state)
|
||||||
else:
|
else:
|
||||||
block = build_random_block_from_state_for_next_slot(spec, state)
|
deposits_for_block = _pull_deposits_from_scenario_state(spec, scenario_state, len(signed_blocks))
|
||||||
|
block = build_random_block_from_state_for_next_slot(spec, state, deposits=deposits_for_block)
|
||||||
_warn_if_empty_operations(block)
|
_warn_if_empty_operations(block)
|
||||||
return block
|
return block
|
||||||
else:
|
else:
|
||||||
|
@ -130,8 +161,9 @@ SYNC_AGGREGATE_PARTICIPATION_BUCKETS = 4
|
||||||
|
|
||||||
def random_block_altair_with_cycling_sync_committee_participation(spec,
|
def random_block_altair_with_cycling_sync_committee_participation(spec,
|
||||||
state,
|
state,
|
||||||
signed_blocks):
|
signed_blocks,
|
||||||
block = random_block(spec, state, signed_blocks)
|
scenario_state):
|
||||||
|
block = random_block(spec, state, signed_blocks, scenario_state)
|
||||||
block_index = len(signed_blocks) % SYNC_AGGREGATE_PARTICIPATION_BUCKETS
|
block_index = len(signed_blocks) % SYNC_AGGREGATE_PARTICIPATION_BUCKETS
|
||||||
fraction_missed = block_index * (1 / SYNC_AGGREGATE_PARTICIPATION_BUCKETS)
|
fraction_missed = block_index * (1 / SYNC_AGGREGATE_PARTICIPATION_BUCKETS)
|
||||||
fraction_participated = 1.0 - fraction_missed
|
fraction_participated = 1.0 - fraction_missed
|
||||||
|
@ -148,7 +180,7 @@ def random_block_altair_with_cycling_sync_committee_participation(spec,
|
||||||
|
|
||||||
# validations
|
# validations
|
||||||
|
|
||||||
def no_op_validation(spec, state):
|
def no_op_validation(_spec, _state):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -213,12 +245,20 @@ def transition_with_random_block(block_randomizer):
|
||||||
|
|
||||||
def _randomized_scenario_setup(state_randomizer):
|
def _randomized_scenario_setup(state_randomizer):
|
||||||
"""
|
"""
|
||||||
Return a sequence of pairs of ("mutation", "validation"),
|
Return a sequence of pairs of ("mutation", "validation").
|
||||||
a function that accepts (spec, state) arguments and performs some change
|
A "mutation" is a function that accepts (``spec``, ``state``, ``stats``) arguments and
|
||||||
and a function that accepts (spec, state) arguments and validates some change was made.
|
allegedly performs some change to the state.
|
||||||
|
A "validation" is a function that accepts (spec, state) arguments and validates some change was made.
|
||||||
|
|
||||||
|
The "mutation" may return some state that should be available to any down-stream transitions
|
||||||
|
across the **entire** scenario.
|
||||||
|
|
||||||
|
The ``stats`` parameter reflects a summary of actions in a given scenario like
|
||||||
|
how many blocks will be produced. This data can be useful to construct a valid
|
||||||
|
pre-state and so is provided at the setup stage.
|
||||||
"""
|
"""
|
||||||
def _skip_epochs(epoch_producer):
|
def _skip_epochs(epoch_producer):
|
||||||
def f(spec, state):
|
def f(spec, state, _stats):
|
||||||
"""
|
"""
|
||||||
The unoptimized spec implementation is too slow to advance via ``next_epoch``.
|
The unoptimized spec implementation is too slow to advance via ``next_epoch``.
|
||||||
Instead, just overwrite the ``state.slot`` and continue...
|
Instead, just overwrite the ``state.slot`` and continue...
|
||||||
|
@ -228,7 +268,7 @@ def _randomized_scenario_setup(state_randomizer):
|
||||||
state.slot += slots_to_skip
|
state.slot += slots_to_skip
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def _simulate_honest_execution(spec, state):
|
def _simulate_honest_execution(spec, state, _stats):
|
||||||
"""
|
"""
|
||||||
Want to start tests not in a leak state; the finality data
|
Want to start tests not in a leak state; the finality data
|
||||||
may not reflect this condition with prior (arbitrary) mutations,
|
may not reflect this condition with prior (arbitrary) mutations,
|
||||||
|
@ -288,14 +328,29 @@ def _iter_temporal(spec, description):
|
||||||
yield i
|
yield i
|
||||||
|
|
||||||
|
|
||||||
|
def _compute_statistics(scenario):
|
||||||
|
block_count = 0
|
||||||
|
for transition in scenario["transitions"]:
|
||||||
|
block_producer = _resolve_ref(transition.get("block_producer", None))
|
||||||
|
if block_producer and block_producer != no_block:
|
||||||
|
block_count += 1
|
||||||
|
return {
|
||||||
|
"block_count": block_count,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def run_generated_randomized_test(spec, state, scenario):
|
def run_generated_randomized_test(spec, state, scenario):
|
||||||
|
stats = _compute_statistics(scenario)
|
||||||
if "setup" not in scenario:
|
if "setup" not in scenario:
|
||||||
state_randomizer = _resolve_ref(scenario.get("state_randomizer", randomize_state))
|
state_randomizer = _resolve_ref(scenario.get("state_randomizer", randomize_state))
|
||||||
scenario["setup"] = _randomized_scenario_setup(state_randomizer)
|
scenario["setup"] = _randomized_scenario_setup(state_randomizer)
|
||||||
|
|
||||||
|
scenario_state = {}
|
||||||
for mutation, validation in scenario["setup"]:
|
for mutation, validation in scenario["setup"]:
|
||||||
mutation(spec, state)
|
additional_state = mutation(spec, state, stats)
|
||||||
validation(spec, state)
|
validation(spec, state)
|
||||||
|
if additional_state:
|
||||||
|
scenario_state.update(additional_state)
|
||||||
|
|
||||||
yield "pre", state
|
yield "pre", state
|
||||||
|
|
||||||
|
@ -309,7 +364,7 @@ def run_generated_randomized_test(spec, state, scenario):
|
||||||
next_slot(spec, state)
|
next_slot(spec, state)
|
||||||
|
|
||||||
block_producer = _resolve_ref(transition["block_producer"])
|
block_producer = _resolve_ref(transition["block_producer"])
|
||||||
block = block_producer(spec, state, blocks)
|
block = block_producer(spec, state, blocks, scenario_state)
|
||||||
if block:
|
if block:
|
||||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||||
blocks.append(signed_block)
|
blocks.append(signed_block)
|
||||||
|
|
Loading…
Reference in New Issue