From e912ed7fca1d62c16979074b28c79fc1072df019 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 15 Mar 2019 13:12:03 +0000 Subject: [PATCH 1/6] Include recently slashed churn in churn queue Addresses #527 in combination with #784. --- specs/core/0_beacon-chain.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index daa1bc108..36d45b31b 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2102,7 +2102,9 @@ def update_validator_registry(state: BeaconState) -> None: activate_validator(state, index, is_genesis=False) # Exit validators within the allowable balance churn - balance_churn = 0 + total_at_start = state.latest_slashed_balances[(current_epoch + 1) % LATEST_SLASHED_EXIT_LENGTH] + total_at_end = state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH] + balance_churn = total_at_end - total_at_start for index, validator in enumerate(state.validator_registry): if validator.exit_epoch == FAR_FUTURE_EPOCH and validator.initiated_exit: # Check the balance churn would be within the allowance From 1236e8e1fa8c9ba235f316f6739aa55672ffcd45 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 15 Mar 2019 13:53:24 +0000 Subject: [PATCH 2/6] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 36d45b31b..ff8b09071 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -595,6 +595,7 @@ The types are defined topologically to aid in facilitating an executable version 'validator_registry': [Validator], 'validator_balances': ['uint64'], 'validator_registry_update_epoch': 'uint64', + 'validator_registry_update_slashed_balances': 'uint64', # Randomness and committees 'latest_randao_mixes': ['bytes32', LATEST_RANDAO_MIXES_LENGTH], @@ -2116,6 +2117,7 @@ def update_validator_registry(state: BeaconState) -> None: exit_validator(state, index) state.validator_registry_update_epoch = current_epoch + state.validator_registry_update_slashed_balances = total_at_end ``` Run the following function: @@ -2164,7 +2166,7 @@ def process_slashings(state: BeaconState) -> None: total_balance = get_total_balance(state, active_validator_indices) # Compute `total_penalties` - total_at_start = state.latest_slashed_balances[(current_epoch + 1) % LATEST_SLASHED_EXIT_LENGTH] + total_at_start = state.validator_registry_update_slashed_balances total_at_end = state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH] total_penalties = total_at_end - total_at_start From 709e0df39f4161e63a1a7877a133b1e121fcb174 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 15 Mar 2019 13:54:33 +0000 Subject: [PATCH 3/6] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index ff8b09071..36c6023e1 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2103,7 +2103,7 @@ def update_validator_registry(state: BeaconState) -> None: activate_validator(state, index, is_genesis=False) # Exit validators within the allowable balance churn - total_at_start = state.latest_slashed_balances[(current_epoch + 1) % LATEST_SLASHED_EXIT_LENGTH] + total_at_start = state.validator_registry_update_slashed_balances total_at_end = state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH] balance_churn = total_at_end - total_at_start for index, validator in enumerate(state.validator_registry): @@ -2166,7 +2166,7 @@ def process_slashings(state: BeaconState) -> None: total_balance = get_total_balance(state, active_validator_indices) # Compute `total_penalties` - total_at_start = state.validator_registry_update_slashed_balances + total_at_start = state.latest_slashed_balances[(current_epoch + 1) % LATEST_SLASHED_EXIT_LENGTH] total_at_end = state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH] total_penalties = total_at_end - total_at_start From e5ff0d59ad22a9bf42acaa0bcf1e7ba646d4b41d Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 16 Mar 2019 11:23:41 +0000 Subject: [PATCH 4/6] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 36c6023e1..26f579233 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -595,7 +595,6 @@ The types are defined topologically to aid in facilitating an executable version 'validator_registry': [Validator], 'validator_balances': ['uint64'], 'validator_registry_update_epoch': 'uint64', - 'validator_registry_update_slashed_balances': 'uint64', # Randomness and committees 'latest_randao_mixes': ['bytes32', LATEST_RANDAO_MIXES_LENGTH], @@ -2103,21 +2102,23 @@ def update_validator_registry(state: BeaconState) -> None: activate_validator(state, index, is_genesis=False) # Exit validators within the allowable balance churn - total_at_start = state.validator_registry_update_slashed_balances - total_at_end = state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH] - balance_churn = total_at_end - total_at_start - for index, validator in enumerate(state.validator_registry): - if validator.exit_epoch == FAR_FUTURE_EPOCH and validator.initiated_exit: - # Check the balance churn would be within the allowance - balance_churn += get_effective_balance(state, index) - if balance_churn > max_balance_churn: - break + if state.current_epoch < state.validator_registry_update_epoch + LATEST_SLASHED_EXIT_LENGTH: + balance_churn = ( + state.latest_slashed_balances[state.validator_registry_update_epoch % LATEST_SLASHED_EXIT_LENGTH] - + state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH] + ) - # Exit validator - exit_validator(state, index) + for index, validator in enumerate(state.validator_registry): + if validator.exit_epoch == FAR_FUTURE_EPOCH and validator.initiated_exit: + # Check the balance churn would be within the allowance + balance_churn += get_effective_balance(state, index) + if balance_churn > max_balance_churn: + break + + # Exit validator + exit_validator(state, index) state.validator_registry_update_epoch = current_epoch - state.validator_registry_update_slashed_balances = total_at_end ``` Run the following function: From 23ef802da5426e0c573417f79f37ecac8500b0b3 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 19 Mar 2019 13:25:34 -0600 Subject: [PATCH 5/6] fix small bug in sytax --- specs/core/0_beacon-chain.md | 2 +- tests/phase0/test_sanity.py | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index aa9fc1e7f..a834a1cde 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2089,7 +2089,7 @@ def update_validator_registry(state: BeaconState) -> None: activate_validator(state, index, is_genesis=False) # Exit validators within the allowable balance churn - if state.current_epoch < state.validator_registry_update_epoch + LATEST_SLASHED_EXIT_LENGTH: + if current_epoch < state.validator_registry_update_epoch + LATEST_SLASHED_EXIT_LENGTH: balance_churn = ( state.latest_slashed_balances[state.validator_registry_update_epoch % LATEST_SLASHED_EXIT_LENGTH] - state.latest_slashed_balances[current_epoch % LATEST_SLASHED_EXIT_LENGTH] diff --git a/tests/phase0/test_sanity.py b/tests/phase0/test_sanity.py index d1811cd00..56c1c1a64 100644 --- a/tests/phase0/test_sanity.py +++ b/tests/phase0/test_sanity.py @@ -316,12 +316,18 @@ def test_attestation(state, pubkeys, privkeys): def test_voluntary_exit(state, pubkeys, privkeys): pre_state = deepcopy(state) - validator_index = get_active_validator_indices(pre_state.validator_registry, get_current_epoch(pre_state))[-1] + validator_index = get_active_validator_indices( + pre_state.validator_registry, + get_current_epoch(pre_state) + )[-1] # 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 - pre_state.validator_registry_update_epoch -= 1 + pre_state.finalized_epoch = get_current_epoch(pre_state) - 1 + for crosslink in pre_state.latest_crosslinks: + crosslink.epoch = pre_state.finalized_epoch + pre_state.validator_registry_update_epoch = pre_state.finalized_epoch - 1 post_state = deepcopy(pre_state) From 24f1139d0938d2360f724cc4de7c1c23160b0157 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Tue, 19 Mar 2019 13:39:45 -0600 Subject: [PATCH 6/6] add explicit test that ensures exists are blocked when too long since registry change --- tests/phase0/test_sanity.py | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/phase0/test_sanity.py b/tests/phase0/test_sanity.py index 56c1c1a64..8c7e7d28b 100644 --- a/tests/phase0/test_sanity.py +++ b/tests/phase0/test_sanity.py @@ -369,6 +369,44 @@ def test_voluntary_exit(state, pubkeys, privkeys): return pre_state, [initiate_exit_block, exit_block], post_state +def test_no_exit_too_long_since_change(state): + pre_state = deepcopy(state) + validator_index = get_active_validator_indices( + pre_state.validator_registry, + get_current_epoch(pre_state) + )[-1] + + # + # setup pre_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 + pre_state.finalized_epoch = get_current_epoch(pre_state) - 1 + for crosslink in pre_state.latest_crosslinks: + crosslink.epoch = pre_state.finalized_epoch + # 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 + ) + # set validator to have previously initiated exit + pre_state.validator_registry[validator_index].initiated_exit = True + + post_state = deepcopy(pre_state) + + # + # Process registry change but ensure no exit + # + block = build_empty_block_for_next_slot(post_state) + block.slot += spec.SLOTS_PER_EPOCH + state_transition(post_state, block) + + assert post_state.validator_registry_update_epoch == get_current_epoch(post_state) - 1 + assert post_state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH + + return pre_state, [block], post_state + + def test_transfer(state, pubkeys, privkeys): pre_state = deepcopy(state) current_epoch = get_current_epoch(pre_state)