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
|
||||
|
||||
|
||||
def prepare_state_and_get_random_deposits(spec, state, rng):
|
||||
num_deposits = rng.randrange(1, spec.MAX_DEPOSITS)
|
||||
def get_random_deposits(spec, state, rng, num_deposits=None):
|
||||
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))]
|
||||
deposits = []
|
||||
|
@ -132,15 +136,19 @@ def prepare_state_and_get_random_deposits(spec, state, rng):
|
|||
signed=True,
|
||||
)
|
||||
|
||||
state.eth1_data.deposit_root = root
|
||||
state.eth1_data.deposit_count += num_deposits
|
||||
|
||||
# Then for that context, build deposits/proofs
|
||||
for i in range(num_deposits):
|
||||
index = len(state.validators) + i
|
||||
deposit, _, _ = deposit_from_context(spec, deposit_data_leaves, index)
|
||||
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
|
||||
|
||||
|
||||
|
@ -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)):
|
||||
# prepare state for deposits before building block
|
||||
deposits = prepare_state_and_get_random_deposits(spec, state, rng)
|
||||
|
||||
def build_random_block_from_state_for_next_slot(spec, state, rng=Random(2188), deposits=None):
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
proposer_slashings = get_random_proposer_slashings(spec, state, rng)
|
||||
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.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
|
||||
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
|
||||
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
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ from typing import Callable
|
|||
from eth2spec.test.helpers.multi_operations import (
|
||||
build_random_block_from_state_for_next_slot,
|
||||
get_random_sync_aggregate,
|
||||
prepare_state_and_get_random_deposits,
|
||||
)
|
||||
from eth2spec.test.helpers.inactivity_scores import (
|
||||
randomize_inactivity_scores,
|
||||
|
@ -28,13 +29,35 @@ from eth2spec.test.helpers.state import (
|
|||
# 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)
|
||||
scenario_state = _randomize_deposit_state(spec, state, stats)
|
||||
return scenario_state
|
||||
|
||||
|
||||
def randomize_state_altair(spec, state):
|
||||
randomize_state(spec, state, exit_fraction=0.1, slash_fraction=0.1)
|
||||
def randomize_state_altair(spec, state, stats):
|
||||
scenario_state = randomize_state(spec, state, stats, exit_fraction=0.1, slash_fraction=0.1)
|
||||
randomize_inactivity_scores(spec, state)
|
||||
return scenario_state
|
||||
|
||||
|
||||
# epochs
|
||||
|
@ -67,7 +90,7 @@ def penultimate_slot_in_epoch(spec):
|
|||
|
||||
# blocks
|
||||
|
||||
def no_block(_spec, _pre_state, _signed_blocks):
|
||||
def no_block(_spec, _pre_state, _signed_blocks, _scenario_state):
|
||||
return None
|
||||
|
||||
|
||||
|
@ -77,9 +100,10 @@ BLOCK_ATTEMPTS = 32
|
|||
|
||||
|
||||
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:
|
||||
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}")
|
||||
|
||||
|
||||
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.
|
||||
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, temp_state)
|
||||
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)
|
||||
return block
|
||||
else:
|
||||
|
@ -130,8 +161,9 @@ SYNC_AGGREGATE_PARTICIPATION_BUCKETS = 4
|
|||
|
||||
def random_block_altair_with_cycling_sync_committee_participation(spec,
|
||||
state,
|
||||
signed_blocks):
|
||||
block = random_block(spec, state, signed_blocks)
|
||||
signed_blocks,
|
||||
scenario_state):
|
||||
block = random_block(spec, state, signed_blocks, scenario_state)
|
||||
block_index = len(signed_blocks) % SYNC_AGGREGATE_PARTICIPATION_BUCKETS
|
||||
fraction_missed = block_index * (1 / SYNC_AGGREGATE_PARTICIPATION_BUCKETS)
|
||||
fraction_participated = 1.0 - fraction_missed
|
||||
|
@ -148,7 +180,7 @@ def random_block_altair_with_cycling_sync_committee_participation(spec,
|
|||
|
||||
# validations
|
||||
|
||||
def no_op_validation(spec, state):
|
||||
def no_op_validation(_spec, _state):
|
||||
return True
|
||||
|
||||
|
||||
|
@ -213,12 +245,20 @@ def transition_with_random_block(block_randomizer):
|
|||
|
||||
def _randomized_scenario_setup(state_randomizer):
|
||||
"""
|
||||
Return a sequence of pairs of ("mutation", "validation"),
|
||||
a function that accepts (spec, state) arguments and performs some change
|
||||
and a function that accepts (spec, state) arguments and validates some change was made.
|
||||
Return a sequence of pairs of ("mutation", "validation").
|
||||
A "mutation" is a function that accepts (``spec``, ``state``, ``stats``) arguments and
|
||||
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 f(spec, state):
|
||||
def f(spec, state, _stats):
|
||||
"""
|
||||
The unoptimized spec implementation is too slow to advance via ``next_epoch``.
|
||||
Instead, just overwrite the ``state.slot`` and continue...
|
||||
|
@ -228,7 +268,7 @@ def _randomized_scenario_setup(state_randomizer):
|
|||
state.slot += slots_to_skip
|
||||
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
|
||||
may not reflect this condition with prior (arbitrary) mutations,
|
||||
|
@ -288,14 +328,29 @@ def _iter_temporal(spec, description):
|
|||
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):
|
||||
stats = _compute_statistics(scenario)
|
||||
if "setup" not in scenario:
|
||||
state_randomizer = _resolve_ref(scenario.get("state_randomizer", randomize_state))
|
||||
scenario["setup"] = _randomized_scenario_setup(state_randomizer)
|
||||
|
||||
scenario_state = {}
|
||||
for mutation, validation in scenario["setup"]:
|
||||
mutation(spec, state)
|
||||
additional_state = mutation(spec, state, stats)
|
||||
validation(spec, state)
|
||||
if additional_state:
|
||||
scenario_state.update(additional_state)
|
||||
|
||||
yield "pre", state
|
||||
|
||||
|
@ -309,7 +364,7 @@ def run_generated_randomized_test(spec, state, scenario):
|
|||
next_slot(spec, state)
|
||||
|
||||
block_producer = _resolve_ref(transition["block_producer"])
|
||||
block = block_producer(spec, state, blocks)
|
||||
block = block_producer(spec, state, blocks, scenario_state)
|
||||
if block:
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
blocks.append(signed_block)
|
||||
|
|
Loading…
Reference in New Issue