From 75a0d60eb3339d457bfc70652cc6cacc8010461f Mon Sep 17 00:00:00 2001 From: protolambda Date: Tue, 19 May 2020 02:25:32 +0200 Subject: [PATCH] cached epoch attestation preparation --- .../eth2spec/test/helpers/attestations.py | 21 +++++++++++ .../pyspec/eth2spec/test/helpers/rewards.py | 37 +++++++++---------- 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/attestations.py b/tests/core/pyspec/eth2spec/test/helpers/attestations.py index e4be6a521..8e0501e4a 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/attestations.py +++ b/tests/core/pyspec/eth2spec/test/helpers/attestations.py @@ -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.utils import bls from eth2spec.utils.ssz.ssz_typing import Bitlist +from lru import LRU def run_attestation_processing(spec, state, attestation, valid=True): @@ -373,6 +374,26 @@ def prepare_state_with_attestations(spec, state, participation_fn=None): 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, make it leaking, and put it in the LRU. + # The input state is likely already cached, so the hash-tree-root is fine. + 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() + + # 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): block.body.shard_transitions = [spec.ShardTransition()] * spec.MAX_SHARDS for attestation in block.body.attestations: diff --git a/tests/core/pyspec/eth2spec/test/helpers/rewards.py b/tests/core/pyspec/eth2spec/test/helpers/rewards.py index fc9f1f93a..42e7a7614 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/rewards.py +++ b/tests/core/pyspec/eth2spec/test/helpers/rewards.py @@ -1,7 +1,7 @@ from random import Random 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.state import next_epoch from eth2spec.utils.ssz.ssz_typing import Container, uint64, List @@ -217,13 +217,13 @@ def run_test_empty(spec, state): def run_test_full_all_correct(spec, state): - prepare_state_with_attestations(spec, state) + cached_prepare_state_with_attestations(spec, state) yield from run_deltas(spec, state) 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: a.aggregation_bits = [rng.choice([True, False]) for _ in a.aggregation_bits] @@ -232,7 +232,7 @@ def run_test_full_but_partial_participation(spec, state, rng=Random(5522)): 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 num_attestations = int(len(state.previous_epoch_attestations) * fraction_filled) @@ -246,7 +246,7 @@ def run_test_half_full(spec, state): 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 state.previous_epoch_attestations = state.previous_epoch_attestations[:1] @@ -256,14 +256,14 @@ def run_test_one_attestation_one_correct(spec, state): def run_test_with_not_yet_activated_validators(spec, state, rng=Random(5555)): set_some_new_deposits(spec, state, rng) - prepare_state_with_attestations(spec, state) + cached_prepare_state_with_attestations(spec, state) yield from run_deltas(spec, state) def run_test_with_exited_validators(spec, state, rng=Random(1337)): exit_random_validators(spec, state, rng) - prepare_state_with_attestations(spec, state) + cached_prepare_state_with_attestations(spec, state) yield from run_deltas(spec, state) @@ -272,14 +272,13 @@ def run_test_with_slashed_validators(spec, state, rng=Random(3322)): exit_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 run_deltas(spec, state) def run_test_some_very_low_effective_balances_that_attested(spec, state): - state.balances - prepare_state_with_attestations(spec, state) + cached_prepare_state_with_attestations(spec, state) # Set some balances to be very low (including 0) assert len(state.validators) >= 5 @@ -290,7 +289,7 @@ def run_test_some_very_low_effective_balances_that_attested(spec, state): 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 attestation = state.previous_epoch_attestations[0] @@ -304,7 +303,7 @@ def run_test_some_very_low_effective_balances_that_did_not_attest(spec, state): 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 num_incorrect = int(fraction_incorrect * len(state.previous_epoch_attestations)) @@ -318,7 +317,7 @@ def run_test_full_fraction_incorrect(spec, state, correct_target, correct_head, def run_test_full_delay_one_slot(spec, state): - prepare_state_with_attestations(spec, state) + cached_prepare_state_with_attestations(spec, state) for a in state.previous_epoch_attestations: a.inclusion_delay += 1 @@ -326,7 +325,7 @@ def run_test_full_delay_one_slot(spec, state): def run_test_full_delay_max_slots(spec, state): - prepare_state_with_attestations(spec, state) + cached_prepare_state_with_attestations(spec, state) for a in state.previous_epoch_attestations: a.inclusion_delay += spec.SLOTS_PER_EPOCH @@ -334,7 +333,7 @@ def run_test_full_delay_max_slots(spec, state): def run_test_full_mixed_delay(spec, state, rng=Random(1234)): - prepare_state_with_attestations(spec, state) + cached_prepare_state_with_attestations(spec, state) for a in state.previous_epoch_attestations: a.inclusion_delay = rng.randint(1, spec.SLOTS_PER_EPOCH) @@ -342,7 +341,7 @@ def run_test_full_mixed_delay(spec, state, rng=Random(1234)): def run_test_proposer_not_in_attestations(spec, state): - prepare_state_with_attestations(spec, state) + cached_prepare_state_with_attestations(spec, state) # Get an attestation where the proposer is not in the committee non_proposer_attestations = [] @@ -357,7 +356,7 @@ def run_test_proposer_not_in_attestations(spec, state): def run_test_duplicate_attestations_at_later_slots(spec, state): - prepare_state_with_attestations(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) @@ -391,7 +390,7 @@ def run_test_duplicate_attestations_at_later_slots(spec, state): def run_test_all_balances_too_low_for_reward(spec, state): - prepare_state_with_attestations(spec, state) + cached_prepare_state_with_attestations(spec, state) for index in range(len(state.validators)): state.validators[index].effective_balance = 10 @@ -404,7 +403,7 @@ def run_test_full_random(spec, state, rng=Random(8020)): exit_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: # ~1/3 have bad target