From 09e8448763fee506c389555a77e72565a6029f43 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Tue, 19 Feb 2019 20:00:46 -0600 Subject: [PATCH 1/4] Turned slashed and initiated_exit into booleans Cuts validator record size from 120 to 106 bytes, and arguably is a simplification --- specs/core/0_beacon-chain.md | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 7db2147955..c70866996d 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -256,11 +256,6 @@ Code snippets appearing in `this style` are to be interpreted as Python code. * The `BASE_REWARD_QUOTIENT` parameter dictates the per-epoch reward. It corresponds to ~2.54% annual interest assuming 10 million participating ETH in every epoch. * The `INACTIVITY_PENALTY_QUOTIENT` equals `INVERSE_SQRT_E_DROP_TIME**2` where `INVERSE_SQRT_E_DROP_TIME := 2**12 epochs` (~18 days) is the time it takes the inactivity penalty to reduce the balance of non-participating [validators](#dfn-validator) to about `1/sqrt(e) ~= 60.6%`. Indeed, the balance retained by offline [validators](#dfn-validator) after `n` epochs is about `(1-1/INACTIVITY_PENALTY_QUOTIENT)**(n**2/2)` so after `INVERSE_SQRT_E_DROP_TIME` epochs it is roughly `(1-1/INACTIVITY_PENALTY_QUOTIENT)**(INACTIVITY_PENALTY_QUOTIENT/2) ~= 1/sqrt(e)`. -### Status flags - -| Name | Value | -| - | - | -| `INITIATED_EXIT` | `2**0` (= 1) | ### Max transactions per block @@ -571,10 +566,10 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'exit_epoch': 'uint64', # Epoch when validator is eligible to withdraw 'withdrawable_epoch': 'uint64', - # Epoch when validator was slashed - 'slashed_epoch': 'uint64', - # Status flags - 'status_flags': 'uint64', + # Did the validator initiate an exit + 'initiated_exit': 'bool', + # Was the validator penalized + 'slashed': 'bool', } ``` @@ -1273,8 +1268,8 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: activation_epoch=FAR_FUTURE_EPOCH, exit_epoch=FAR_FUTURE_EPOCH, withdrawable_epoch=FAR_FUTURE_EPOCH, - slashed_epoch=FAR_FUTURE_EPOCH, - status_flags=0, + initiated_exit=False, + slashed=False, ) # Note: In phase 2 registry indices that have been withdrawn for a long time will be recycled. @@ -1314,7 +1309,7 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: Note that this function mutates ``state``. """ validator = state.validator_registry[index] - validator.status_flags |= INITIATED_EXIT + validator.initiated_exit = True ``` #### `exit_validator` @@ -1351,7 +1346,7 @@ def slash_validator(state: BeaconState, index: ValidatorIndex) -> None: whistleblower_reward = get_effective_balance(state, index) // WHISTLEBLOWER_REWARD_QUOTIENT state.validator_balances[whistleblower_index] += whistleblower_reward state.validator_balances[index] -= whistleblower_reward - validator.slashed_epoch = get_current_epoch(state) + validator.slashed = True validator.withdrawable_epoch = get_current_epoch(state) + LATEST_SLASHED_EXIT_LENGTH ``` @@ -1644,7 +1639,7 @@ For each `proposer_slashing` in `block.body.proposer_slashings`: * Verify that `proposer_slashing.proposal_1.slot == proposer_slashing.proposal_2.slot`. * Verify that `proposer_slashing.proposal_1.shard == proposer_slashing.proposal_2.shard`. * Verify that `proposer_slashing.proposal_1.block_root != proposer_slashing.proposal_2.block_root`. -* Verify that `proposer.slashed_epoch > get_current_epoch(state)`. +* Verify that `proposer.slashed == False`. * Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=signed_root(proposer_slashing.proposal_1, "signature"), signature=proposer_slashing.proposal_1.signature, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_1.slot), DOMAIN_PROPOSAL))`. * Verify that `bls_verify(pubkey=proposer.pubkey, message_hash=signed_root(proposer_slashing.proposal_2, "signature"), signature=proposer_slashing.proposal_2.signature, domain=get_domain(state.fork, slot_to_epoch(proposer_slashing.proposal_2.slot), DOMAIN_PROPOSAL))`. * Run `slash_validator(state, proposer_slashing.proposer_index)`. @@ -1661,7 +1656,7 @@ For each `attester_slashing` in `block.body.attester_slashings`: * Verify that `is_double_vote(slashable_attestation_1.data, slashable_attestation_2.data)` or `is_surround_vote(slashable_attestation_1.data, slashable_attestation_2.data)`. * Verify that `verify_slashable_attestation(state, slashable_attestation_1)`. * Verify that `verify_slashable_attestation(state, slashable_attestation_2)`. -* Let `slashable_indices = [index for index in slashable_attestation_1.validator_indices if index in slashable_attestation_2.validator_indices and state.validator_registry[index].slashed_epoch > get_current_epoch(state)]`. +* Let `slashable_indices = [index for index in slashable_attestation_1.validator_indices if index in slashable_attestation_2.validator_indices and state.validator_registry[index].slashed == False]`. * Verify that `len(slashable_indices) >= 1`. * Run `slash_validator(state, index)` for each `index` in `slashable_indices`. @@ -1895,7 +1890,7 @@ Case 2: `epochs_since_finality > 4`: * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_boundary_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_head_attester_indices`, loses `base_reward(state, index)`. -* Any [active validator](#dfn-active-validator) `index` with `validator.slashed_epoch <= current_epoch`, loses `2 * inactivity_penalty(state, index, epochs_since_finality) + base_reward(state, index)`. +* Any [active validator](#dfn-active-validator) `index` with `validator.slashed == True`, loses `2 * inactivity_penalty(state, index, epochs_since_finality) + base_reward(state, index)`. * Any [validator](#dfn-validator) `index` in `previous_epoch_attester_indices` loses `base_reward(state, index) - base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // inclusion_distance(state, index)` ##### Attestation inclusion @@ -1974,7 +1969,7 @@ def update_validator_registry(state: BeaconState) -> None: # Exit validators within the allowable balance churn balance_churn = 0 for index, validator in enumerate(state.validator_registry): - if validator.exit_epoch > get_entry_exit_effect_epoch(current_epoch) and validator.status_flags & INITIATED_EXIT: + if validator.exit_epoch > get_entry_exit_effect_epoch(current_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: @@ -2015,7 +2010,7 @@ def process_slashings(state: BeaconState) -> None: total_balance = sum(get_effective_balance(state, i) for i in active_validator_indices) for index, validator in enumerate(state.validator_registry): - if current_epoch == validator.slashed_epoch + LATEST_SLASHED_EXIT_LENGTH // 2: + if validator.slashed and current_epoch == validator.withdrawable_epoch - LATEST_SLASHED_EXIT_LENGTH // 2: epoch_index = current_epoch % LATEST_SLASHED_EXIT_LENGTH total_at_start = state.latest_slashed_balances[(epoch_index + 1) % LATEST_SLASHED_EXIT_LENGTH] total_at_end = state.latest_slashed_balances[epoch_index] From 9ac61e57c006f2361416226b5bbb62a331f72fc0 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 20 Feb 2019 07:18:12 +0000 Subject: [PATCH 2/4] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index c70866996d..e2136d9632 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -18,7 +18,6 @@ - [Time parameters](#time-parameters) - [State list lengths](#state-list-lengths) - [Reward and penalty quotients](#reward-and-penalty-quotients) - - [Status flags](#status-flags) - [Max transactions per block](#max-transactions-per-block) - [Signature domains](#signature-domains) - [Data structures](#data-structures) @@ -568,7 +567,7 @@ The following data structures are defined as [SimpleSerialize (SSZ)](https://git 'withdrawable_epoch': 'uint64', # Did the validator initiate an exit 'initiated_exit': 'bool', - # Was the validator penalized + # Was the validator slashed 'slashed': 'bool', } ``` From e25e87f00340b9e9414b6e408233471b5d6fd0fb Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 20 Feb 2019 07:45:19 +0000 Subject: [PATCH 3/4] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index e2136d9632..9463d3b19e 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -89,7 +89,7 @@ - [`is_double_vote`](#is_double_vote) - [`is_surround_vote`](#is_surround_vote) - [`integer_squareroot`](#integer_squareroot) - - [`get_entry_exit_effect_epoch`](#get_entry_exit_effect_epoch) + - [`get_delayed_activation_exit_epoch`](#get_delayed_activation_exit_epoch) - [`bls_verify`](#bls_verify) - [`bls_verify_multiple`](#bls_verify_multiple) - [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys) @@ -1208,13 +1208,12 @@ def integer_squareroot(n: int) -> int: return x ``` -### `get_entry_exit_effect_epoch` +### `get_delayed_activation_exit_epoch` ```python -def get_entry_exit_effect_epoch(epoch: Epoch) -> Epoch: +def get_delayed_activation_exit_epoch(epoch: Epoch) -> Epoch: """ - An entry or exit triggered in the ``epoch`` given by the input takes effect at - the epoch given by the output. + Return the epoch at which an activation or exit triggered in ``epoch`` takes effect. """ return epoch + 1 + ACTIVATION_EXIT_DELAY ``` @@ -1296,7 +1295,7 @@ def activate_validator(state: BeaconState, index: ValidatorIndex, is_genesis: bo """ validator = state.validator_registry[index] - validator.activation_epoch = GENESIS_EPOCH if is_genesis else get_entry_exit_effect_epoch(get_current_epoch(state)) + validator.activation_epoch = GENESIS_EPOCH if is_genesis else get_delayed_activation_exit_epoch(get_current_epoch(state)) ``` #### `initiate_validator_exit` @@ -1322,10 +1321,10 @@ def exit_validator(state: BeaconState, index: ValidatorIndex) -> None: validator = state.validator_registry[index] # The following updates only occur if not previous exited - if validator.exit_epoch <= get_entry_exit_effect_epoch(get_current_epoch(state)): + if validator.exit_epoch <= get_delayed_activation_exit_epoch(get_current_epoch(state)): return - validator.exit_epoch = get_entry_exit_effect_epoch(get_current_epoch(state)) + validator.exit_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) ``` #### `slash_validator` @@ -1746,7 +1745,7 @@ Verify that `len(block.body.voluntary_exits) <= MAX_VOLUNTARY_EXITS`. For each `exit` in `block.body.voluntary_exits`: * Let `validator = state.validator_registry[exit.validator_index]`. -* Verify that `validator.exit_epoch > get_entry_exit_effect_epoch(get_current_epoch(state))`. +* Verify that `validator.exit_epoch > get_delayed_activation_exit_epoch(get_current_epoch(state))`. * Verify that `get_current_epoch(state) >= exit.epoch`. * Verify that `bls_verify(pubkey=validator.pubkey, message_hash=signed_root(exit, "signature"), signature=exit.signature, domain=get_domain(state.fork, exit.epoch, DOMAIN_EXIT))`. * Run `initiate_validator_exit(state, exit.validator_index)`. @@ -1956,7 +1955,7 @@ def update_validator_registry(state: BeaconState) -> None: # Activate validators within the allowable balance churn balance_churn = 0 for index, validator in enumerate(state.validator_registry): - if validator.activation_epoch > get_entry_exit_effect_epoch(current_epoch) and state.validator_balances[index] >= MAX_DEPOSIT_AMOUNT: + if validator.activation_epoch > get_delayed_activation_exit_epoch(current_epoch) and state.validator_balances[index] >= MAX_DEPOSIT_AMOUNT: # Check the balance churn would be within the allowance balance_churn += get_effective_balance(state, index) if balance_churn > max_balance_churn: @@ -1968,7 +1967,7 @@ def update_validator_registry(state: BeaconState) -> None: # Exit validators within the allowable balance churn balance_churn = 0 for index, validator in enumerate(state.validator_registry): - if validator.exit_epoch > get_entry_exit_effect_epoch(current_epoch) and validator.initiated_exit: + if validator.exit_epoch > get_delayed_activation_exit_epoch(current_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: From 5f59aad00d5ae5e719d084972c73ff2a8238fa59 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 21 Feb 2019 22:34:11 +0100 Subject: [PATCH 4/4] Clarify use of `FAR_FUTURE_EPOCH` as a flag --- specs/core/0_beacon-chain.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 9463d3b19e..51cc9d9f54 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1955,7 +1955,7 @@ def update_validator_registry(state: BeaconState) -> None: # Activate validators within the allowable balance churn balance_churn = 0 for index, validator in enumerate(state.validator_registry): - if validator.activation_epoch > get_delayed_activation_exit_epoch(current_epoch) and state.validator_balances[index] >= MAX_DEPOSIT_AMOUNT: + if validator.activation_epoch == FAR_FUTURE_EPOCH and state.validator_balances[index] >= MAX_DEPOSIT_AMOUNT: # Check the balance churn would be within the allowance balance_churn += get_effective_balance(state, index) if balance_churn > max_balance_churn: @@ -1967,7 +1967,7 @@ def update_validator_registry(state: BeaconState) -> None: # Exit validators within the allowable balance churn balance_churn = 0 for index, validator in enumerate(state.validator_registry): - if validator.exit_epoch > get_delayed_activation_exit_epoch(current_epoch) and validator.initiated_exit: + if validator.activation_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: @@ -2029,7 +2029,7 @@ def process_exit_queue(state: BeaconState) -> None: def eligible(index): validator = state.validator_registry[index] # Filter out dequeued validators - if validator.withdrawable_epoch < FAR_FUTURE_EPOCH: + if validator.withdrawable_epoch != FAR_FUTURE_EPOCH: return False # Dequeue if the minimum amount of time has passed else: