From 529cf4223e221bb8e782f76eb7b0c22aa051e7bc Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 2 Apr 2019 16:00:36 +1100 Subject: [PATCH] add previous and current crosslinks --- specs/core/0_beacon-chain.md | 38 ++++++++++--------- .../test_process_attestation.py | 2 +- .../test_process_proposer_slashing.py | 2 +- tests/phase0/conftest.py | 3 +- tests/phase0/helpers.py | 7 +--- tests/phase0/test_sanity.py | 6 ++- 6 files changed, 31 insertions(+), 27 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index d08828692..1032a905a 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -610,7 +610,8 @@ The types are defined topologically to aid in facilitating an executable version 'finalized_root': 'bytes32', # Recent state - 'latest_crosslinks': [Crosslink, SHARD_COUNT], + 'current_crosslinks': [Crosslink, SHARD_COUNT], + 'previous_crosslinks': [Crosslink, SHARD_COUNT], 'latest_block_roots': ['bytes32', SLOTS_PER_HISTORICAL_ROOT], 'latest_state_roots': ['bytes32', SLOTS_PER_HISTORICAL_ROOT], 'latest_active_index_roots': ['bytes32', LATEST_ACTIVE_INDEX_ROOTS_LENGTH], @@ -1554,7 +1555,8 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], finalized_root=ZERO_HASH, # Recent state - latest_crosslinks=Vector([Crosslink(epoch=GENESIS_EPOCH, crosslink_data_root=ZERO_HASH) for _ in range(SHARD_COUNT)]), + current_crosslinks=Vector([Crosslink(epoch=GENESIS_EPOCH, crosslink_data_root=ZERO_HASH) for _ in range(SHARD_COUNT)]), + previous_crosslinks=Vector([Crosslink(epoch=GENESIS_EPOCH, crosslink_data_root=ZERO_HASH) for _ in range(SHARD_COUNT)]), latest_block_roots=Vector([ZERO_HASH for _ in range(SLOTS_PER_HISTORICAL_ROOT)]), latest_state_roots=Vector([ZERO_HASH for _ in range(SLOTS_PER_HISTORICAL_ROOT)]), latest_active_index_roots=Vector([ZERO_HASH for _ in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH)]), @@ -1758,10 +1760,14 @@ def get_previous_epoch_matching_head_attestations(state: BeaconState) -> List[Pe **Note**: Total balances computed for the previous epoch might be marginally different than the actual total balances during the previous epoch transition. Due to the tight bound on validator churn each epoch and small per-epoch rewards/penalties, the potential balance difference is very low and only marginally affects consensus safety. ```python -def get_winning_root_and_participants(state: BeaconState, shard: Shard) -> Tuple[Bytes32, List[ValidatorIndex]]: +def get_winning_root_and_participants(state: BeaconState, slot: Slot, shard: Shard) -> Tuple[Bytes32, List[ValidatorIndex]]: all_attestations = state.current_epoch_attestations + state.previous_epoch_attestations + crosslinks = state.current_crosslinks if slot_to_epoch(slot) == get_current_epoch(state) else state.previous_crosslinks + valid_attestations = [ - a for a in all_attestations if a.data.previous_crosslink == state.latest_crosslinks[shard] + a for a in all_attestations + if a.data.previous_crosslink == crosslinks[shard] and + a.data.shard == shard and a.data.slot == slot ] all_roots = [a.data.crosslink_data_root for a in valid_attestations] @@ -1856,16 +1862,20 @@ def process_crosslinks(state: BeaconState) -> None: current_epoch = get_current_epoch(state) previous_epoch = max(current_epoch - 1, GENESIS_EPOCH) next_epoch = current_epoch + 1 + next_previous_crosslinks = [crosslink for crosslink in state.current_crosslinks] + for slot in range(get_epoch_start_slot(previous_epoch), get_epoch_start_slot(next_epoch)): for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot): - winning_root, participants = get_winning_root_and_participants(state, shard) + winning_root, participants = get_winning_root_and_participants(state, slot, shard) participating_balance = get_total_balance(state, participants) total_balance = get_total_balance(state, crosslink_committee) if 3 * participating_balance >= 2 * total_balance: - state.latest_crosslinks[shard] = Crosslink( - epoch=min(slot_to_epoch(slot), state.latest_crosslinks[shard].epoch + MAX_CROSSLINK_EPOCHS), - crosslink_data_root=winning_root + state.current_crosslinks[shard] = Crosslink( + epoch=min(slot_to_epoch(slot), state.current_crosslinks[shard].epoch + MAX_CROSSLINK_EPOCHS), + crosslink_data_root=winning_root, ) + + state.previous_crosslinks = next_previous_crosslinks ``` #### Eth1 data @@ -1972,7 +1982,7 @@ def get_crosslink_deltas(state: BeaconState) -> Tuple[List[Gwei], List[Gwei]]: current_epoch_start_slot = get_epoch_start_slot(get_current_epoch(state)) for slot in range(previous_epoch_start_slot, current_epoch_start_slot): for crosslink_committee, shard in get_crosslink_committees_at_slot(state, slot): - winning_root, participants = get_winning_root_and_participants(state, shard) + winning_root, participants = get_winning_root_and_participants(state, slot, shard) participating_balance = get_total_balance(state, participants) total_balance = get_total_balance(state, crosslink_committee) for index in crosslink_committee: @@ -2327,14 +2337,8 @@ def process_attestation(state: BeaconState, attestation: Attestation) -> None: # Check crosslink data assert attestation.data.crosslink_data_root == ZERO_HASH # [to be removed in phase 1] - assert state.latest_crosslinks[attestation.data.shard] in { - attestation.data.previous_crosslink, # Case 1: latest crosslink matches previous crosslink - Crosslink( # Case 2: latest crosslink matches current crosslink - crosslink_data_root=attestation.data.crosslink_data_root, - epoch=min(slot_to_epoch(attestation.data.slot), - attestation.data.previous_crosslink.epoch + MAX_CROSSLINK_EPOCHS) - ), - } + crosslinks = state.current_crosslinks if slot_to_epoch(attestation.data.slot) == get_current_epoch(state) else state.previous_crosslinks + assert crosslinks[attestation.data.shard] == attestation.data.previous_crosslink # Check signature and bitfields assert verify_indexed_attestation(state, convert_to_indexed(state, attestation)) diff --git a/tests/phase0/block_processing/test_process_attestation.py b/tests/phase0/block_processing/test_process_attestation.py index ca6933ce7..c946feb05 100644 --- a/tests/phase0/block_processing/test_process_attestation.py +++ b/tests/phase0/block_processing/test_process_attestation.py @@ -124,7 +124,7 @@ def test_bad_previous_crosslink(state): attestation = get_valid_attestation(state) state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY - state.latest_crosslinks[attestation.data.shard].epoch += 10 + state.current_crosslinks[attestation.data.shard].epoch += 10 pre_state, post_state = run_attestation_processing(state, attestation, False) diff --git a/tests/phase0/block_processing/test_process_proposer_slashing.py b/tests/phase0/block_processing/test_process_proposer_slashing.py index 467d2164b..b5aedc8bb 100644 --- a/tests/phase0/block_processing/test_process_proposer_slashing.py +++ b/tests/phase0/block_processing/test_process_proposer_slashing.py @@ -11,7 +11,7 @@ from tests.phase0.helpers import ( get_valid_proposer_slashing, ) -# mark entire file as 'header' +# mark entire file as 'proposer_slashings' pytestmark = pytest.mark.proposer_slashings diff --git a/tests/phase0/conftest.py b/tests/phase0/conftest.py index 36a087941..809d1239e 100644 --- a/tests/phase0/conftest.py +++ b/tests/phase0/conftest.py @@ -28,7 +28,8 @@ def overwrite_spec_config(config): if field == "LATEST_RANDAO_MIXES_LENGTH": spec.BeaconState.fields['latest_randao_mixes'][1] = config[field] elif field == "SHARD_COUNT": - spec.BeaconState.fields['latest_crosslinks'][1] = config[field] + spec.BeaconState.fields['current_crosslinks'][1] = config[field] + spec.BeaconState.fields['previous_crosslinks'][1] = config[field] elif field == "SLOTS_PER_HISTORICAL_ROOT": spec.BeaconState.fields['latest_block_roots'][1] = config[field] spec.BeaconState.fields['latest_state_roots'][1] = config[field] diff --git a/tests/phase0/helpers.py b/tests/phase0/helpers.py index e5e335d80..083d31b80 100644 --- a/tests/phase0/helpers.py +++ b/tests/phase0/helpers.py @@ -95,10 +95,6 @@ def create_genesis_state(num_validators, deposit_data_leaves=None): def force_registry_change_at_next_epoch(state): - # artificially trigger registry update at next epoch transition - state.finalized_epoch = get_current_epoch(state) - 1 - for crosslink in state.latest_crosslinks: - crosslink.epoch = state.finalized_epoch state.validator_registry_update_epoch = state.finalized_epoch - 1 @@ -149,6 +145,7 @@ def build_attestation_data(state, slot, shard): else: justified_block_root = state.current_justified_root + crosslinks = state.current_crosslinks if slot_to_epoch(slot) == get_current_epoch(state) else state.previous_crosslinks return AttestationData( slot=slot, shard=shard, @@ -157,7 +154,7 @@ def build_attestation_data(state, slot, shard): source_root=justified_block_root, target_root=epoch_boundary_root, crosslink_data_root=spec.ZERO_HASH, - previous_crosslink=deepcopy(state.latest_crosslinks[shard]), + previous_crosslink=deepcopy(crosslinks[shard]), ) diff --git a/tests/phase0/test_sanity.py b/tests/phase0/test_sanity.py index 3b4497ca5..a7a6b9961 100644 --- a/tests/phase0/test_sanity.py +++ b/tests/phase0/test_sanity.py @@ -24,6 +24,7 @@ from build.phase0.spec import ( advance_slot, cache_state, set_balance, + slot_to_epoch, verify_merkle_branch, hash, ) @@ -254,6 +255,7 @@ def test_voluntary_exit(state): # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH + pre_state.finalized_epoch = slot_to_epoch(pre_state.slot) - 3 # artificially trigger registry update at next epoch transition force_registry_change_at_next_epoch(pre_state) @@ -309,12 +311,12 @@ def test_no_exit_churn_too_long_since_change(state): # # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH - # artificially trigger registry update at next epoch transition - force_registry_change_at_next_epoch(pre_state) # make epochs since registry update greater than LATEST_SLASHED_EXIT_LENGTH pre_state.validator_registry_update_epoch = ( get_current_epoch(pre_state) - spec.LATEST_SLASHED_EXIT_LENGTH ) + # artificially trigger registry update at next epoch transition + force_registry_change_at_next_epoch(pre_state) # set validator to have previously initiated exit pre_state.validator_registry[validator_index].initiated_exit = True