From 1aaa0030fc90c4e8557ea2c7bb08d4c86d4085bd Mon Sep 17 00:00:00 2001 From: vbuterin Date: Thu, 28 Mar 2019 08:58:18 -0500 Subject: [PATCH 01/45] Withdrawal queue -> exit queue --- specs/core/0_beacon-chain.md | 181 +++++++++++------------------------ 1 file changed, 55 insertions(+), 126 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 2c0bc2554..14b4f566a 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -98,9 +98,7 @@ - [Routines for updating validator status](#routines-for-updating-validator-status) - [`activate_validator`](#activate_validator) - [`initiate_validator_exit`](#initiate_validator_exit) - - [`exit_validator`](#exit_validator) - [`slash_validator`](#slash_validator) - - [`prepare_validator_for_withdrawal`](#prepare_validator_for_withdrawal) - [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) - [Deposit arguments](#deposit-arguments) - [Withdrawal credentials](#withdrawal-credentials) @@ -121,8 +119,8 @@ - [Justification and finalization](#justification-and-finalization) - [Crosslinks](#crosslinks-1) - [Apply rewards](#apply-rewards) - - [Ejections](#ejections) - - [Validator registry and shuffling seed data](#validator-registry-and-shuffling-seed-data) + - [Balance-driven status transitions](#balance-driven-status-transitions) + - [Validator registry and start shard](#validator-registry-and-start-shard) - [Slashings and exit queue](#slashings-and-exit-queue) - [Final updates](#final-updates) - [Per-slot processing](#per-slot-processing) @@ -182,7 +180,6 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | - | - | | `SHARD_COUNT` | `2**10` (= 1,024) | | `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) | -| `MAX_BALANCE_CHURN_QUOTIENT` | `2**5` (= 32) | | `MAX_SLASHABLE_ATTESTATION_PARTICIPANTS` | `2**12` (= 4,096) | | `MAX_EXIT_DEQUEUES_PER_EPOCH` | `2**2` (= 4) | | `SHUFFLE_ROUND_COUNT` | 90 | @@ -418,14 +415,14 @@ The types are defined topologically to aid in facilitating an executable version 'pubkey': 'bytes48', # Withdrawal credentials 'withdrawal_credentials': 'bytes32', + # Epoch when became eligible for activation + 'activation_eligibility_epoch': 'uint64', # Epoch when validator activated 'activation_epoch': 'uint64', # Epoch when validator exited 'exit_epoch': 'uint64', # Epoch when validator is eligible to withdraw 'withdrawable_epoch': 'uint64', - # Did the validator initiate an exit - 'initiated_exit': 'bool', # Was the validator slashed 'slashed': 'bool', # Rounded balance @@ -596,6 +593,10 @@ The types are defined topologically to aid in facilitating an executable version # Randomness and committees 'latest_randao_mixes': ['bytes32', LATEST_RANDAO_MIXES_LENGTH], 'latest_start_shard': 'uint64', + + # Exit queue + 'exit_epoch': 'uint64', + 'exit_queue_filled': 'uint64' # Finality 'previous_epoch_attestations': [PendingAttestation], @@ -1352,22 +1353,20 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: Note that this function mutates ``state``. """ validator = state.validator_registry[index] - validator.initiated_exit = True -``` - -#### `exit_validator` - -```python -def exit_validator(state: BeaconState, index: ValidatorIndex) -> None: - """ - Exit the validator with the given ``index``. - Note that this function mutates ``state``. - """ - validator = state.validator_registry[index] - - # Update validator exit epoch if not previously exited + # Operation is a no-op if validator is already in the queue if validator.exit_epoch == FAR_FUTURE_EPOCH: - validator.exit_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) + # Update exit queue counters + if state.exit_epoch < get_delayed_activation_exit_epoch(get_current_epoch(state)): + state.exit_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) + if state.exit_queue_filled >= MAX_EXIT_DEQUEUES_PER_EPOCH: + state.exit_epoch += 1 + state.exit_queue_filled = 0 + # Set validator exit epoch and withdrawable epoch + if validator.exit_epoch > state.exit_epoch: + validator.exit_epoch = state.exit_epoch + validator.withdrawable_epoch = validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY + # Extend queue + state.exit_queue_filled += 1 ``` #### `slash_validator` @@ -1379,7 +1378,7 @@ def slash_validator(state: BeaconState, index: ValidatorIndex) -> None: Note that this function mutates ``state``. """ validator = state.validator_registry[index] - exit_validator(state, index) + initiate_validator_exit(state, index) state.latest_slashed_balances[get_current_epoch(state) % LATEST_SLASHED_EXIT_LENGTH] += get_effective_balance(state, index) whistleblower_index = get_beacon_proposer_index(state, state.slot) @@ -1390,19 +1389,6 @@ def slash_validator(state: BeaconState, index: ValidatorIndex) -> None: validator.withdrawable_epoch = get_current_epoch(state) + LATEST_SLASHED_EXIT_LENGTH ``` -#### `prepare_validator_for_withdrawal` - -```python -def prepare_validator_for_withdrawal(state: BeaconState, index: ValidatorIndex) -> None: - """ - Set the validator with the given ``index`` as withdrawable - ``MIN_VALIDATOR_WITHDRAWABILITY_DELAY`` after the current epoch. - Note that this function mutates ``state``. - """ - validator = state.validator_registry[index] - validator.withdrawable_epoch = get_current_epoch(state) + MIN_VALIDATOR_WITHDRAWABILITY_DELAY -``` - ## Ethereum 1.0 deposit contract The initial deployment phases of Ethereum 2.0 are implemented without consensus changes to Ethereum 1.0. A deposit contract at address `DEPOSIT_CONTRACT_ADDRESS` is added to Ethereum 1.0 for deposits of ETH to the beacon chain. Validator balances will be withdrawable to the shards in phase 2, i.e. when the EVM2.0 is deployed and the shards have state. @@ -1512,6 +1498,10 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], # Randomness and committees latest_randao_mixes=Vector([ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH)]), latest_start_shard=GENESIS_START_SHARD, + + # Exit queue + exit_epoch=GENESIS_EPOCH, + exit_queue_filled=0, # Finality previous_epoch_attestations=[], @@ -1690,16 +1680,16 @@ def get_previous_total_balance(state: BeaconState) -> Gwei: ``` ```python -def get_attesting_indices(state: BeaconState, attestations: List[PendingAttestation]) -> List[ValidatorIndex]: +def get_unslashed_attesting_indices(state: BeaconState, attestations: List[PendingAttestation]) -> List[ValidatorIndex]: output = set() for a in attestations: output = output.union(get_attestation_participants(state, a.data, a.aggregation_bitfield)) - return sorted(list(output)) + return sorted(filter(lambda index: not state.validator_registry[index].is_slashed, list(output))) ``` ```python def get_attesting_balance(state: BeaconState, attestations: List[PendingAttestation]) -> Gwei: - return get_total_balance(state, get_attesting_indices(state, attestations)) + return get_total_balance(state, get_unslashed_attesting_indices(state, attestations)) ``` ```python @@ -1747,7 +1737,7 @@ def get_winning_root_and_participants(state: BeaconState, shard: Shard) -> Tuple # lexicographically higher hash winning_root = max(all_roots, key=lambda r: (get_attesting_balance(state, get_attestations_for(r)), r)) - return winning_root, get_attesting_indices(state, get_attestations_for(winning_root)) + return winning_root, get_unslashed_attesting_indices(state, get_attestations_for(winning_root)) ``` ```python @@ -1904,7 +1894,7 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[ for index in eligible_validators: base_reward = get_base_reward(state, index) # Expected FFG source - if index in get_attesting_indices(state, state.previous_epoch_attestations): + if index in get_unslashed_attesting_indices(state, state.previous_epoch_attestations): rewards[index] += base_reward * total_attesting_balance // total_balance # Inclusion speed bonus rewards[index] += ( @@ -1914,17 +1904,17 @@ def get_justification_and_finalization_deltas(state: BeaconState) -> Tuple[List[ else: penalties[index] += base_reward # Expected FFG target - if index in get_attesting_indices(state, boundary_attestations): + if index in get_unslashed_attesting_indices(state, boundary_attestations): rewards[index] += base_reward * boundary_attesting_balance // total_balance else: penalties[index] += get_inactivity_penalty(state, index, epochs_since_finality) # Expected head - if index in get_attesting_indices(state, matching_head_attestations): + if index in get_unslashed_attesting_indices(state, matching_head_attestations): rewards[index] += base_reward * matching_head_balance // total_balance else: penalties[index] += base_reward # Proposer bonus - if index in get_attesting_indices(state, state.previous_epoch_attestations): + if index in get_unslashed_attesting_indices(state, state.previous_epoch_attestations): proposer_index = get_beacon_proposer_index(state, inclusion_slot(state, index)) rewards[proposer_index] += base_reward // ATTESTATION_INCLUSION_REWARD_QUOTIENT # Take away max rewards if we're not finalizing @@ -1973,72 +1963,24 @@ def apply_rewards(state: BeaconState) -> None: ) ``` -#### Ejections +#### Balance-driven status transitions -Run `process_ejections(state)`. +Run `process_balance_driven_status_transitions(state)`. ```python def process_ejections(state: BeaconState) -> None: """ Iterate through the validator registry - and eject active validators with balance below ``EJECTION_BALANCE``. + and deposit or eject active validators with sufficiently high or low balances """ - for index in get_active_validator_indices(state.validator_registry, get_current_epoch(state)): - if get_balance(state, index) < EJECTION_BALANCE: + for index, validator in enumeratE(state.validator_registry): + if validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and balance >= MAX_DEPOSIT_AMOUNT: + state.activation_eligibility_epoch = get_current_epoch(state) + if is_active(validator, get_current_epoch(state)) and get_balance(state, index) < EJECTION_BALANCE: initiate_validator_exit(state, index) ``` -#### Validator registry and shuffling seed data - -```python -def update_validator_registry(state: BeaconState) -> None: - """ - Update validator registry. - Note that this function mutates ``state``. - """ - current_epoch = get_current_epoch(state) - # The active validators - active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch) - # The total effective balance of active validators - total_balance = get_total_balance(state, active_validator_indices) - - # The maximum balance churn in Gwei (for deposits and exits separately) - max_balance_churn = max( - MAX_DEPOSIT_AMOUNT, - total_balance // (2 * MAX_BALANCE_CHURN_QUOTIENT) - ) - - # Activate validators within the allowable balance churn - balance_churn = 0 - for index, validator in enumerate(state.validator_registry): - if validator.activation_epoch == FAR_FUTURE_EPOCH and get_balance(state, 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: - break - - # Activate validator - activate_validator(state, index, is_genesis=False) - - # Exit validators within the allowable balance churn - 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] - ) - - 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 -``` +#### Validator registry and start shard Run the following function: @@ -2046,7 +1988,18 @@ Run the following function: def update_registry(state: BeaconState) -> None: # Check if we should update, and if so, update if state.finalized_epoch > state.validator_registry_update_epoch: - update_validator_registry(state) + # Validator indices that could be activated + indices_for_activation = sorted( + filter( + lambda index: state.validator_registry[index].activation_epoch == FAR_FUTURE_EPOCH + get_active_validator_indices(state.validator_registry, current_epoch), + ), + key=lambda index: state.validator_registry[index].activation_eligibility_epoch + ) + for index in indices_for_activation[:MAX_EXIT_DEQUEUES_PER_EPOCH]: + activate_validator(state, index, is_genesis=False) + + state.validator_registry_update_epoch = current_epoch state.latest_start_shard = ( state.latest_start_shard + get_current_epoch_committee_count(state) @@ -2057,7 +2010,7 @@ def update_registry(state: BeaconState) -> None: #### Slashings and exit queue -Run `process_slashings(state)` and `process_exit_queue(state)`: +Run `process_slashings(state)`: ```python def process_slashings(state: BeaconState) -> None: @@ -2083,30 +2036,6 @@ def process_slashings(state: BeaconState) -> None: decrease_balance(state, index, penalty) ``` -```python -def process_exit_queue(state: BeaconState) -> None: - """ - Process the exit queue. - Note that this function mutates ``state``. - """ - def eligible(index): - validator = state.validator_registry[index] - # Filter out dequeued validators - if validator.withdrawable_epoch != FAR_FUTURE_EPOCH: - return False - # Dequeue if the minimum amount of time has passed - else: - return get_current_epoch(state) >= validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY - - eligible_indices = filter(eligible, list(range(len(state.validator_registry)))) - # Sort in order of exit epoch, and validators that exit within the same epoch exit in order of validator index - sorted_indices = sorted(eligible_indices, key=lambda index: state.validator_registry[index].exit_epoch) - for dequeues, index in enumerate(sorted_indices): - if dequeues >= MAX_EXIT_DEQUEUES_PER_EPOCH: - break - prepare_validator_for_withdrawal(state, index) -``` - #### Final updates Run the following function: From deb0e32590fed25280e6923b8d9054b941beccb7 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Thu, 28 Mar 2019 10:51:36 -0500 Subject: [PATCH 02/45] Fixes to make Justin happy --- specs/core/0_beacon-chain.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 14b4f566a..120aa89d2 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1309,10 +1309,10 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: validator = Validator( pubkey=pubkey, withdrawal_credentials=deposit.data.withdrawal_credentials, + activation_eligibility_epoch=FAR_FUTURE_EPOCH, activation_epoch=FAR_FUTURE_EPOCH, exit_epoch=FAR_FUTURE_EPOCH, withdrawable_epoch=FAR_FUTURE_EPOCH, - initiated_exit=False, slashed=False, high_balance=0 ) @@ -1973,7 +1973,7 @@ def process_ejections(state: BeaconState) -> None: Iterate through the validator registry and deposit or eject active validators with sufficiently high or low balances """ - for index, validator in enumeratE(state.validator_registry): + for index, validator in enumerate(state.validator_registry): if validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and balance >= MAX_DEPOSIT_AMOUNT: state.activation_eligibility_epoch = get_current_epoch(state) if is_active(validator, get_current_epoch(state)) and get_balance(state, index) < EJECTION_BALANCE: @@ -2284,8 +2284,6 @@ def process_voluntary_exit(state: BeaconState, exit: VoluntaryExit) -> None: assert is_active_validator(validator, get_current_epoch(state)) # Verify the validator has not yet exited assert validator.exit_epoch == FAR_FUTURE_EPOCH - # Verify the validator has not initiated an exit - assert validator.initiated_exit is False # Exits must specify an epoch when they become valid; they are not valid before then assert get_current_epoch(state) >= exit.epoch # Verify the validator has been active long enough From aa4bbcc1c82ba9435cdee00c57fca66268fdb229 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 29 Mar 2019 00:43:28 +0800 Subject: [PATCH 03/45] Bugfix --- specs/core/0_beacon-chain.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 120aa89d2..094206f13 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -596,7 +596,7 @@ The types are defined topologically to aid in facilitating an executable version # Exit queue 'exit_epoch': 'uint64', - 'exit_queue_filled': 'uint64' + 'exit_queue_filled': 'uint64', # Finality 'previous_epoch_attestations': [PendingAttestation], @@ -1974,9 +1974,10 @@ def process_ejections(state: BeaconState) -> None: and deposit or eject active validators with sufficiently high or low balances """ for index, validator in enumerate(state.validator_registry): + balance = get_balance(state, index) if validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and balance >= MAX_DEPOSIT_AMOUNT: state.activation_eligibility_epoch = get_current_epoch(state) - if is_active(validator, get_current_epoch(state)) and get_balance(state, index) < EJECTION_BALANCE: + if is_active_validator(validator, get_current_epoch(state)) and balance < EJECTION_BALANCE: initiate_validator_exit(state, index) ``` @@ -1986,12 +1987,13 @@ Run the following function: ```python def update_registry(state: BeaconState) -> None: + current_epoch = get_current_epoch(state) # Check if we should update, and if so, update if state.finalized_epoch > state.validator_registry_update_epoch: # Validator indices that could be activated indices_for_activation = sorted( filter( - lambda index: state.validator_registry[index].activation_epoch == FAR_FUTURE_EPOCH + lambda index: state.validator_registry[index].activation_epoch == FAR_FUTURE_EPOCH, get_active_validator_indices(state.validator_registry, current_epoch), ), key=lambda index: state.validator_registry[index].activation_eligibility_epoch From a2dae9a8e0fced5b4b57aef1944b153a6bfb0091 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 29 Mar 2019 15:26:26 +0800 Subject: [PATCH 04/45] Fix after merging --- specs/core/0_beacon-chain.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 7719b4d13..16141b399 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1381,15 +1381,19 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: # Operation is a no-op if validator is already in the queue if validator.exit_epoch == FAR_FUTURE_EPOCH: # Update exit queue counters - if state.exit_epoch < get_delayed_activation_exit_epoch(get_current_epoch(state)): - state.exit_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) + delayed_activation_exit_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) + if state.exit_epoch < delayed_activation_exit_epoch: + state.exit_epoch = delayed_activation_exit_epoch + if state.exit_queue_filled >= MAX_EXIT_DEQUEUES_PER_EPOCH: state.exit_epoch += 1 state.exit_queue_filled = 0 + # Set validator exit epoch and withdrawable epoch if validator.exit_epoch > state.exit_epoch: validator.exit_epoch = state.exit_epoch validator.withdrawable_epoch = validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY + # Extend queue state.exit_queue_filled += 1 ``` @@ -1402,7 +1406,7 @@ def slash_validator(state: BeaconState, slashed_index: ValidatorIndex, whistlebl Slash the validator with index ``slashed_index``. Note that this function mutates ``state``. """ - initiate_validator_exit(state, index) + initiate_validator_exit(state, slashed_index) state.validator_registry[slashed_index].slashed = True state.validator_registry[slashed_index].withdrawable_epoch = get_current_epoch(state) + LATEST_SLASHED_EXIT_LENGTH slashed_balance = get_effective_balance(state, slashed_index) @@ -1997,7 +2001,7 @@ def apply_rewards(state: BeaconState) -> None: Run `process_balance_driven_status_transitions(state)`. ```python -def process_ejections(state: BeaconState) -> None: +def process_balance_driven_status_transitions(state: BeaconState) -> None: """ Iterate through the validator registry and deposit or eject active validators with sufficiently high or low balances @@ -2005,7 +2009,8 @@ def process_ejections(state: BeaconState) -> None: for index, validator in enumerate(state.validator_registry): balance = get_balance(state, index) if validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and balance >= MAX_DEPOSIT_AMOUNT: - state.activation_eligibility_epoch = get_current_epoch(state) + state.activation_eligibility_epoch = get_current_epoch(state) + if is_active_validator(validator, get_current_epoch(state)) and balance < EJECTION_BALANCE: initiate_validator_exit(state, index) ``` From 15498f22ef5c4ea9afb371a90e74040bb7e08c48 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sat, 30 Mar 2019 19:26:44 -0500 Subject: [PATCH 05/45] Fixed exit epoch conditional --- specs/core/0_beacon-chain.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 16141b399..e5102c56d 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1390,9 +1390,8 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: state.exit_queue_filled = 0 # Set validator exit epoch and withdrawable epoch - if validator.exit_epoch > state.exit_epoch: - validator.exit_epoch = state.exit_epoch - validator.withdrawable_epoch = validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY + validator.exit_epoch = state.exit_epoch + validator.withdrawable_epoch = validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY # Extend queue state.exit_queue_filled += 1 From 2529cb1d74f8a73f0d327480d9ebc3c052d639f9 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 3 Apr 2019 22:53:41 +0400 Subject: [PATCH 06/45] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index e5102c56d..bdce36d9b 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -589,7 +589,6 @@ The types are defined topologically to aid in facilitating an executable version # Validator registry 'validator_registry': [Validator], 'balances': ['uint64'], - 'validator_registry_update_epoch': 'uint64', # Randomness and committees 'latest_randao_mixes': ['bytes32', LATEST_RANDAO_MIXES_LENGTH], @@ -1525,7 +1524,6 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], # Validator registry validator_registry=[], balances=[], - validator_registry_update_epoch=GENESIS_EPOCH, # Randomness and committees latest_randao_mixes=Vector([ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH)]), @@ -2022,7 +2020,11 @@ Run the following function: def update_registry(state: BeaconState) -> None: current_epoch = get_current_epoch(state) # Check if we should update, and if so, update - if state.finalized_epoch > state.validator_registry_update_epoch: + + activations_since_finalization = len([index in state.validator_registry if + state.validator_registry[index].activation_epoch > state.finalized_epoch + ACTIVATION_EXIT_DELAY + ]) + if MAX_EXIT_DEQUEUES_PER_EPOCH > activations_since_finalization: # Validator indices that could be activated indices_for_activation = sorted( filter( @@ -2031,10 +2033,9 @@ def update_registry(state: BeaconState) -> None: ), key=lambda index: state.validator_registry[index].activation_eligibility_epoch ) - for index in indices_for_activation[:MAX_EXIT_DEQUEUES_PER_EPOCH]: + for index in indices_for_activation[:MAX_EXIT_DEQUEUES_PER_EPOCH - activations_since_finalization]: activate_validator(state, index, is_genesis=False) - state.validator_registry_update_epoch = current_epoch state.latest_start_shard = ( state.latest_start_shard + get_current_epoch_committee_count(state) From 169579ce3ba5e09572afd6a00c8ec7ae3211e11f Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 6 Apr 2019 20:45:11 +1100 Subject: [PATCH 07/45] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index bdce36d9b..deb53fd4b 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -183,7 +183,8 @@ Code snippets appearing in `this style` are to be interpreted as Python code. | `SHARD_COUNT` | `2**10` (= 1,024) | | `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) | | `MAX_ATTESTATION_PARTICIPANTS` | `2**12` (= 4,096) | -| `MAX_EXIT_DEQUEUES_PER_EPOCH` | `2**2` (= 4) | +| `MAX_EXITS_PER_EPOCH` | `2**2` (= 4) | +| `MAX_ACTIVATIONS_PER_FINALIZED_EPOCH` | `2**2` (= 4) | | `SHUFFLE_ROUND_COUNT` | 90 | * For the safety of crosslinks `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `SLOTS_PER_EPOCH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes of at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.) @@ -1384,7 +1385,7 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: if state.exit_epoch < delayed_activation_exit_epoch: state.exit_epoch = delayed_activation_exit_epoch - if state.exit_queue_filled >= MAX_EXIT_DEQUEUES_PER_EPOCH: + if state.exit_queue_filled >= MAX_EXITS_PER_EPOCH: state.exit_epoch += 1 state.exit_queue_filled = 0 @@ -2018,23 +2019,13 @@ Run the following function: ```python def update_registry(state: BeaconState) -> None: - current_epoch = get_current_epoch(state) - # Check if we should update, and if so, update + activation_queue = sorted([validator in enumerate(validators) if + validator.activation_epoch == FAR_FUTURE_EPOCH and + validator.activation_eligibility_epoch > get_delayed_activation_exit_epoch(state.finalized_epoch) + ], key=lambda index: state.validator_registry[index].activation_eligibility_epoch) - activations_since_finalization = len([index in state.validator_registry if - state.validator_registry[index].activation_epoch > state.finalized_epoch + ACTIVATION_EXIT_DELAY - ]) - if MAX_EXIT_DEQUEUES_PER_EPOCH > activations_since_finalization: - # Validator indices that could be activated - indices_for_activation = sorted( - filter( - lambda index: state.validator_registry[index].activation_epoch == FAR_FUTURE_EPOCH, - get_active_validator_indices(state.validator_registry, current_epoch), - ), - key=lambda index: state.validator_registry[index].activation_eligibility_epoch - ) - for index in indices_for_activation[:MAX_EXIT_DEQUEUES_PER_EPOCH - activations_since_finalization]: - activate_validator(state, index, is_genesis=False) + for index in activation_queue[:MAX_ACTIVATIONS_PER_FINALIZED_EPOCH]: + activate_validator(state, index, is_genesis=False) state.latest_start_shard = ( state.latest_start_shard + @@ -2042,8 +2033,6 @@ def update_registry(state: BeaconState) -> None: ) % SHARD_COUNT ``` -**Invariant**: the active index root that is hashed into the shuffling seed actually is the `hash_tree_root` of the validator set that is used for that epoch. - #### Slashings and exit queue Run `process_slashings(state)`: From 7f0a93fc3e095ea4ae1d9892934a89c33a8723ed Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 6 Apr 2019 21:07:03 +1100 Subject: [PATCH 08/45] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index deb53fd4b..4d054c2b6 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -122,8 +122,8 @@ - [Crosslinks](#crosslinks-1) - [Apply rewards](#apply-rewards) - [Balance-driven status transitions](#balance-driven-status-transitions) - - [Validator registry and start shard](#validator-registry-and-start-shard) - - [Slashings and exit queue](#slashings-and-exit-queue) + - [Activation queue and start shard](#activation-queue-and-start-shard) + - [Slashings](#slashings) - [Final updates](#final-updates) - [Per-slot processing](#per-slot-processing) - [Per-block processing](#per-block-processing) @@ -2013,7 +2013,7 @@ def process_balance_driven_status_transitions(state: BeaconState) -> None: initiate_validator_exit(state, index) ``` -#### Validator registry and start shard +#### Activation queue and start shard Run the following function: @@ -2021,7 +2021,8 @@ Run the following function: def update_registry(state: BeaconState) -> None: activation_queue = sorted([validator in enumerate(validators) if validator.activation_epoch == FAR_FUTURE_EPOCH and - validator.activation_eligibility_epoch > get_delayed_activation_exit_epoch(state.finalized_epoch) + validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH and + validator.activation_eligibility_epoch > state.finalized_epoch ], key=lambda index: state.validator_registry[index].activation_eligibility_epoch) for index in activation_queue[:MAX_ACTIVATIONS_PER_FINALIZED_EPOCH]: @@ -2033,7 +2034,7 @@ def update_registry(state: BeaconState) -> None: ) % SHARD_COUNT ``` -#### Slashings and exit queue +#### Slashings Run `process_slashings(state)`: From 63412d9b9e2752280e48703adf262eb6b0285121 Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 6 Apr 2019 22:13:56 +1100 Subject: [PATCH 09/45] 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 4d054c2b6..f947c7d49 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1565,7 +1565,7 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], process_deposit(state, deposit) # Process genesis activations - for validator_index, _ in enumerate(state.validator_registry): + for validator_index in state.validator_registry: if get_effective_balance(state, validator_index) >= MAX_DEPOSIT_AMOUNT: activate_validator(state, validator_index, is_genesis=True) @@ -2019,7 +2019,7 @@ Run the following function: ```python def update_registry(state: BeaconState) -> None: - activation_queue = sorted([validator in enumerate(validators) if + activation_queue = sorted([validator for _, validator in enumerate(validators) if validator.activation_epoch == FAR_FUTURE_EPOCH and validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH and validator.activation_eligibility_epoch > state.finalized_epoch From 5ea5746fdd09462a94d8ea68f1e3f777849e2aac Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 7 Apr 2019 01:24:50 +1100 Subject: [PATCH 10/45] Fix `get_genesis_beacon_state` and minor refactoring --- specs/core/0_beacon-chain.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index f947c7d49..3e9189739 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -831,11 +831,11 @@ def get_permuted_index(index: int, list_size: int, seed: Bytes32) -> int: ```python def get_split_offset(list_size: int, chunks: int, index: int) -> int: - """ - Returns a value such that for a list L, chunk count k and index i, - split(L, k)[i] == L[get_split_offset(len(L), k, i): get_split_offset(len(L), k, i+1)] - """ - return (list_size * index) // chunks + """ + Returns a value such that for a list L, chunk count k and index i, + split(L, k)[i] == L[get_split_offset(len(L), k, i): get_split_offset(len(L), k, i+1)] + """ + return (list_size * index) // chunks ``` ### `get_epoch_committee_count` @@ -1042,7 +1042,7 @@ def verify_merkle_branch(leaf: Bytes32, proof: List[Bytes32], depth: int, index: ```python def get_crosslink_committee_for_attestation(state: BeaconState, - attestation_data: AttestationData) -> List[ValidatorIndex]: + attestation_data: AttestationData) -> List[ValidatorIndex]: # Find the committee in the list with the desired shard crosslink_committees = get_crosslink_committees_at_slot(state, attestation_data.slot) @@ -1565,7 +1565,7 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], process_deposit(state, deposit) # Process genesis activations - for validator_index in state.validator_registry: + for validator_index in range(len(state.validator_registry)): if get_effective_balance(state, validator_index) >= MAX_DEPOSIT_AMOUNT: activate_validator(state, validator_index, is_genesis=True) @@ -2019,7 +2019,8 @@ Run the following function: ```python def update_registry(state: BeaconState) -> None: - activation_queue = sorted([validator for _, validator in enumerate(validators) if + activation_queue = sorted([ + validator for validator in state.validator_registry if validator.activation_epoch == FAR_FUTURE_EPOCH and validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH and validator.activation_eligibility_epoch > state.finalized_epoch From ebba3f5891fd8c686820f77d6f35f576f70d2b94 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 7 Apr 2019 01:59:10 +1100 Subject: [PATCH 11/45] Fix typo --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 2501343ff..079971fc8 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1721,7 +1721,7 @@ def get_unslashed_attesting_indices(state: BeaconState, attestations: List[Pendi output = set() for a in attestations: output = output.union(get_attestation_participants(state, a.data, a.aggregation_bitfield)) - return sorted(filter(lambda index: not state.validator_registry[index].is_slashed, list(output))) + return sorted(filter(lambda index: not state.validator_registry[index].slashed, list(output))) ``` ```python From 00872e0e9260fc63a80e9535d4949503ed8b71be Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 7 Apr 2019 02:09:07 +1100 Subject: [PATCH 12/45] Updated tests 1. No Validator.initiated_exit field. Fix the related tests. 2. Update state_transition flow - rename `process_ejections` to `process_balance_driven_status_transitions` and remove `process_exit_queue` 3. Linter, formatting --- .../test_process_attestation.py | 5 +-- .../test_process_attester_slashing.py | 1 - .../test_process_block_header.py | 2 +- .../test_process_proposer_slashing.py | 1 - .../block_processing/test_voluntary_exit.py | 38 +------------------ tests/phase0/helpers.py | 3 +- tests/phase0/test_sanity.py | 18 +++------ utils/phase0/state_transition.py | 3 +- 8 files changed, 13 insertions(+), 58 deletions(-) diff --git a/tests/phase0/block_processing/test_process_attestation.py b/tests/phase0/block_processing/test_process_attestation.py index ca6933ce7..1e8ee4488 100644 --- a/tests/phase0/block_processing/test_process_attestation.py +++ b/tests/phase0/block_processing/test_process_attestation.py @@ -7,7 +7,6 @@ from build.phase0.state_transition import ( state_transition, ) from build.phase0.spec import ( - ZERO_HASH, get_current_epoch, process_attestation, slot_to_epoch, @@ -102,7 +101,7 @@ def test_bad_source_root(state): attestation = get_valid_attestation(state) state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY - attestation.data.source_root = b'\x42'*32 + attestation.data.source_root = b'\x42' * 32 pre_state, post_state = run_attestation_processing(state, attestation, False) @@ -113,7 +112,7 @@ def test_non_zero_crosslink_data_root(state): attestation = get_valid_attestation(state) state.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY - attestation.data.crosslink_data_root = b'\x42'*32 + attestation.data.crosslink_data_root = b'\x42' * 32 pre_state, post_state = run_attestation_processing(state, attestation, False) diff --git a/tests/phase0/block_processing/test_process_attester_slashing.py b/tests/phase0/block_processing/test_process_attester_slashing.py index 06f214c4b..cf58ee244 100644 --- a/tests/phase0/block_processing/test_process_attester_slashing.py +++ b/tests/phase0/block_processing/test_process_attester_slashing.py @@ -31,7 +31,6 @@ def run_attester_slashing_processing(state, attester_slashing, valid=True): slashed_index = attester_slashing.attestation_1.custody_bit_0_indices[0] slashed_validator = post_state.validator_registry[slashed_index] - assert not slashed_validator.initiated_exit assert slashed_validator.slashed assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH diff --git a/tests/phase0/block_processing/test_process_block_header.py b/tests/phase0/block_processing/test_process_block_header.py index 4981b656c..508a70b19 100644 --- a/tests/phase0/block_processing/test_process_block_header.py +++ b/tests/phase0/block_processing/test_process_block_header.py @@ -54,7 +54,7 @@ def test_invalid_slot(state): def test_invalid_previous_block_root(state): block = build_empty_block_for_next_slot(state) - block.previous_block_root = b'\12'*32 # invalid prev root + block.previous_block_root = b'\12' * 32 # invalid prev root pre_state, post_state = run_block_header_processing(state, block, valid=False) return pre_state, block, None diff --git a/tests/phase0/block_processing/test_process_proposer_slashing.py b/tests/phase0/block_processing/test_process_proposer_slashing.py index 467d2164b..317829518 100644 --- a/tests/phase0/block_processing/test_process_proposer_slashing.py +++ b/tests/phase0/block_processing/test_process_proposer_slashing.py @@ -30,7 +30,6 @@ def run_proposer_slashing_processing(state, proposer_slashing, valid=True): process_proposer_slashing(post_state, proposer_slashing) slashed_validator = post_state.validator_registry[proposer_slashing.proposer_index] - assert not slashed_validator.initiated_exit assert slashed_validator.slashed assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH diff --git a/tests/phase0/block_processing/test_voluntary_exit.py b/tests/phase0/block_processing/test_voluntary_exit.py index 6adc81464..7627f1f0b 100644 --- a/tests/phase0/block_processing/test_voluntary_exit.py +++ b/tests/phase0/block_processing/test_voluntary_exit.py @@ -47,8 +47,8 @@ def test_success(state): # process_voluntary_exit(post_state, voluntary_exit) - assert not pre_state.validator_registry[validator_index].initiated_exit - assert post_state.validator_registry[validator_index].initiated_exit + assert pre_state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH + assert post_state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH return pre_state, voluntary_exit, post_state @@ -111,37 +111,6 @@ def test_validator_already_exited(state): return pre_state, voluntary_exit, None -def test_validator_already_initiated_exit(state): - pre_state = deepcopy(state) - # - # setup pre_state - # - # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow validator able to exit - pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH - - current_epoch = get_current_epoch(pre_state) - validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0] - privkey = pubkey_to_privkey[pre_state.validator_registry[validator_index].pubkey] - - # but validator already has initiated exit - pre_state.validator_registry[validator_index].initiated_exit = True - - # - # build voluntary exit - # - voluntary_exit = build_voluntary_exit( - pre_state, - current_epoch, - validator_index, - privkey, - ) - - with pytest.raises(AssertionError): - process_voluntary_exit(pre_state, voluntary_exit) - - return pre_state, voluntary_exit, None - - def test_validator_not_active_long_enough(state): pre_state = deepcopy(state) # @@ -151,9 +120,6 @@ def test_validator_not_active_long_enough(state): validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0] privkey = pubkey_to_privkey[pre_state.validator_registry[validator_index].pubkey] - # but validator already has initiated exit - pre_state.validator_registry[validator_index].initiated_exit = True - # # build voluntary exit # diff --git a/tests/phase0/helpers.py b/tests/phase0/helpers.py index 33f394def..e60c7c64c 100644 --- a/tests/phase0/helpers.py +++ b/tests/phase0/helpers.py @@ -25,7 +25,6 @@ from build.phase0.spec import ( get_attestation_participants, get_block_root, get_crosslink_committee_for_attestation, - get_crosslink_committees_at_slot, get_current_epoch, get_domain, get_empty_block, @@ -249,7 +248,7 @@ def get_valid_proposer_slashing(state): def get_valid_attester_slashing(state): attestation_1 = get_valid_attestation(state) attestation_2 = deepcopy(attestation_1) - attestation_2.data.target_root = b'\x01'*32 + attestation_2.data.target_root = b'\x01' * 32 return AttesterSlashing( attestation_1=convert_to_indexed(state, attestation_1), diff --git a/tests/phase0/test_sanity.py b/tests/phase0/test_sanity.py index 90825242f..a2ce8928b 100644 --- a/tests/phase0/test_sanity.py +++ b/tests/phase0/test_sanity.py @@ -128,11 +128,9 @@ def test_proposer_slashing(state): block.body.proposer_slashings.append(proposer_slashing) state_transition(test_state, block) - assert not state.validator_registry[validator_index].initiated_exit assert not state.validator_registry[validator_index].slashed slashed_validator = test_state.validator_registry[validator_index] - assert not slashed_validator.initiated_exit assert slashed_validator.slashed assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH @@ -154,11 +152,9 @@ def test_attester_slashing(state): block.body.attester_slashings.append(attester_slashing) state_transition(test_state, block) - assert not state.validator_registry[validator_index].initiated_exit assert not state.validator_registry[validator_index].slashed slashed_validator = test_state.validator_registry[validator_index] - assert not slashed_validator.initiated_exit assert slashed_validator.slashed assert slashed_validator.exit_epoch < spec.FAR_FUTURE_EPOCH assert slashed_validator.withdrawable_epoch < spec.FAR_FUTURE_EPOCH @@ -316,9 +312,7 @@ def test_voluntary_exit(state): initiate_exit_block.body.voluntary_exits.append(voluntary_exit) state_transition(post_state, initiate_exit_block) - assert not pre_state.validator_registry[validator_index].initiated_exit - assert post_state.validator_registry[validator_index].initiated_exit - assert post_state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH + assert post_state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH # # Process within epoch transition @@ -350,8 +344,6 @@ def test_no_exit_churn_too_long_since_change(state): 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) @@ -362,8 +354,10 @@ def test_no_exit_churn_too_long_since_change(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].activation_eligibility_epoch == spec.FAR_FUTURE_EPOCH assert post_state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH + assert post_state.exit_queue_filled == pre_state.exit_queue_filled + assert post_state.exit_epoch == pre_state.exit_epoch return pre_state, [block], post_state @@ -419,7 +413,7 @@ def test_transfer(state): return pre_state, [block], post_state -def test_ejection(state): +def test_balance_driven_status_transitions(state): pre_state = deepcopy(state) current_epoch = get_current_epoch(pre_state) @@ -438,7 +432,7 @@ def test_ejection(state): block.slot += spec.SLOTS_PER_EPOCH state_transition(post_state, block) - assert post_state.validator_registry[validator_index].initiated_exit == True + assert post_state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH return pre_state, [block], post_state diff --git a/utils/phase0/state_transition.py b/utils/phase0/state_transition.py index 2c420014f..3a3601ddb 100644 --- a/utils/phase0/state_transition.py +++ b/utils/phase0/state_transition.py @@ -94,10 +94,9 @@ def process_epoch_transition(state: BeaconState) -> None: spec.process_crosslinks(state) spec.maybe_reset_eth1_period(state) spec.apply_rewards(state) - spec.process_ejections(state) + spec.process_balance_driven_status_transitions(state) spec.update_registry(state) spec.process_slashings(state) - spec.process_exit_queue(state) spec.finish_epoch_update(state) From 47464f28dc014ea2bb2a20121a1353fc80180174 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 7 Apr 2019 08:45:43 +1000 Subject: [PATCH 13/45] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 079971fc8..8a7b24cec 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1371,7 +1371,11 @@ 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_delayed_activation_exit_epoch(get_current_epoch(state)) + if is_genesis: + validator.activation_eligibility_epoch = GENESIS_EPOCH + validator.activation_epoch = GENESIS_EPOCH + else: + validator.activation_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) ``` #### `initiate_validator_exit` @@ -2027,9 +2031,8 @@ Run the following function: def update_registry(state: BeaconState) -> None: activation_queue = sorted([ validator for validator in state.validator_registry if - validator.activation_epoch == FAR_FUTURE_EPOCH and validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH and - validator.activation_eligibility_epoch > state.finalized_epoch + validator.activation_epoch >= get_delayed_activation_exit_epoch(state.finalized_epoch) ], key=lambda index: state.validator_registry[index].activation_eligibility_epoch) for index in activation_queue[:MAX_ACTIVATIONS_PER_FINALIZED_EPOCH]: From 4630b136dacba2689a1277de3fd4d7015eb0f132 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 7 Apr 2019 10:03:07 +1000 Subject: [PATCH 14/45] Fix/Remove pointless assertion --- tests/phase0/test_sanity.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/phase0/test_sanity.py b/tests/phase0/test_sanity.py index a2ce8928b..612227cfa 100644 --- a/tests/phase0/test_sanity.py +++ b/tests/phase0/test_sanity.py @@ -354,7 +354,6 @@ def test_no_exit_churn_too_long_since_change(state): block.slot += spec.SLOTS_PER_EPOCH state_transition(post_state, block) - assert post_state.validator_registry[validator_index].activation_eligibility_epoch == spec.FAR_FUTURE_EPOCH assert post_state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH assert post_state.exit_queue_filled == pre_state.exit_queue_filled assert post_state.exit_epoch == pre_state.exit_epoch From 846e2d61478b1f55fc8675a45953bbf7ecc4b335 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 7 Apr 2019 11:06:14 +1000 Subject: [PATCH 15/45] Remove `force_registry_change_at_next_epoch` --- tests/phase0/helpers.py | 8 -------- tests/phase0/test_sanity.py | 9 --------- 2 files changed, 17 deletions(-) diff --git a/tests/phase0/helpers.py b/tests/phase0/helpers.py index e60c7c64c..34f2f8d7f 100644 --- a/tests/phase0/helpers.py +++ b/tests/phase0/helpers.py @@ -95,14 +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 - - def build_empty_block_for_next_slot(state): empty_block = get_empty_block() empty_block.slot = state.slot + 1 diff --git a/tests/phase0/test_sanity.py b/tests/phase0/test_sanity.py index 612227cfa..7ed31b7bd 100644 --- a/tests/phase0/test_sanity.py +++ b/tests/phase0/test_sanity.py @@ -39,7 +39,6 @@ from build.phase0.utils.merkle_minimal import ( from tests.phase0.helpers import ( build_deposit_data, build_empty_block_for_next_slot, - force_registry_change_at_next_epoch, get_valid_attestation, get_valid_attester_slashing, get_valid_proposer_slashing, @@ -285,8 +284,6 @@ 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 - # artificially trigger registry update at next epoch transition - force_registry_change_at_next_epoch(pre_state) post_state = deepcopy(pre_state) @@ -338,12 +335,6 @@ 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 - ) post_state = deepcopy(pre_state) From f4753d6157841eadff7ed65bc21a479ce889c546 Mon Sep 17 00:00:00 2001 From: JSON <49416440+JSON@users.noreply.github.com> Date: Fri, 12 Apr 2019 18:32:36 -0500 Subject: [PATCH 16/45] Update rpc-interface.md (#907) --- specs/networking/rpc-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/networking/rpc-interface.md b/specs/networking/rpc-interface.md index fa49bcd75..85ebe0bf6 100644 --- a/specs/networking/rpc-interface.md +++ b/specs/networking/rpc-interface.md @@ -9,7 +9,7 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL", NOT", "SHOULD", # Dependencies -This specification assumes familiarity with the [Messaging](./messaging.md), [Node Identification](./node-identification), and [Beacon Chain](../core/0_beacon-chain.md) specifications. +This specification assumes familiarity with the [Messaging](./messaging.md), [Node Identification](./node-identification.md), and [Beacon Chain](../core/0_beacon-chain.md) specifications. # Specification From 0b6d6f2c51eb3ff66373ec5b9d86db22711ee285 Mon Sep 17 00:00:00 2001 From: JSON <49416440+JSON@users.noreply.github.com> Date: Fri, 12 Apr 2019 18:33:34 -0500 Subject: [PATCH 17/45] Update 0_beacon-chain-validator.md (#909) --- specs/validator/0_beacon-chain-validator.md | 32 ++++++++++----------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 7b03a910a..1fbe08ef4 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -1,6 +1,6 @@ # Ethereum 2.0 Phase 0 -- Honest Validator -__NOTICE__: This document is a work-in-progress for researchers and implementers. This is an accompanying document to [Ethereum 2.0 Phase 0 -- The Beacon Chain](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md) that describes the expected actions of a "validator" participating in the Ethereum 2.0 protocol. +__NOTICE__: This document is a work-in-progress for researchers and implementers. This is an accompanying document to [Ethereum 2.0 Phase 0 -- The Beacon Chain](../core/0_beacon-chain.md) that describes the expected actions of a "validator" participating in the Ethereum 2.0 protocol. ## Table of Contents @@ -66,7 +66,7 @@ A validator is an entity that participates in the consensus of the Ethereum 2.0 ## Prerequisites -All terminology, constants, functions, and protocol mechanics defined in the [Phase 0 -- The Beacon Chain](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md) doc are requisite for this document and used throughout. Please see the Phase 0 doc before continuing and use as a reference throughout. +All terminology, constants, functions, and protocol mechanics defined in the [Phase 0 -- The Beacon Chain](../core/0_beacon-chain.md) doc are requisite for this document and used throughout. Please see the Phase 0 doc before continuing and use as a reference throughout. ## Constants @@ -84,7 +84,7 @@ A validator must initialize many parameters locally before submitting a deposit #### BLS public key -Validator public keys are [G1 points](https://github.com/ethereum/eth2.0-specs/blob/master/specs/bls_signature.md#g1-points) on the [BLS12-381 curve](https://z.cash/blog/new-snark-curve). A private key, `privkey`, must be securely generated along with the resultant `pubkey`. This `privkey` must be "hot", that is, constantly available to sign data throughout the lifetime of the validator. +Validator public keys are [G1 points](../bls_signature.md#g1-points) on the [BLS12-381 curve](https://z.cash/blog/new-snark-curve). A private key, `privkey`, must be securely generated along with the resultant `pubkey`. This `privkey` must be "hot", that is, constantly available to sign data throughout the lifetime of the validator. #### BLS withdrawal key @@ -96,7 +96,7 @@ The validator constructs their `withdrawal_credentials` via the following: ### Submit deposit -In phase 0, all incoming validator deposits originate from the Ethereum 1.0 PoW chain. Deposits are made to the [deposit contract](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#ethereum-10-deposit-contract) located at `DEPOSIT_CONTRACT_ADDRESS`. +In phase 0, all incoming validator deposits originate from the Ethereum 1.0 PoW chain. Deposits are made to the [deposit contract](../core/0_beacon-chain.md#ethereum-10-deposit-contract) located at `DEPOSIT_CONTRACT_ADDRESS`. To submit a deposit: @@ -114,13 +114,13 @@ Deposits cannot be processed into the beacon chain until the eth1.0 block in whi ### Validator index -Once a validator has been processed and added to the beacon state's `validator_registry`, the validator's `validator_index` is defined by the index into the registry at which the [`ValidatorRecord`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#validator) contains the `pubkey` specified in the validator's deposit. A validator's `validator_index` is guaranteed to not change from the time of initial deposit until the validator exits and fully withdraws. This `validator_index` is used throughout the specification to dictate validator roles and responsibilities at any point and should be stored locally. +Once a validator has been processed and added to the beacon state's `validator_registry`, the validator's `validator_index` is defined by the index into the registry at which the [`ValidatorRecord`](../core/0_beacon-chain.md#validator) contains the `pubkey` specified in the validator's deposit. A validator's `validator_index` is guaranteed to not change from the time of initial deposit until the validator exits and fully withdraws. This `validator_index` is used throughout the specification to dictate validator roles and responsibilities at any point and should be stored locally. ### Activation In normal operation, the validator is quickly activated at which point the validator is added to the shuffling and begins validation after an additional `ACTIVATION_EXIT_DELAY` epochs (25.6 minutes). -The function [`is_active_validator`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#is_active_validator) can be used to check if a validator is active during a given shuffling epoch. Note that the `BeaconState` contains a field `current_shuffling_epoch` which dictates from which epoch the current active validators are taken. Usage is as follows: +The function [`is_active_validator`](../core/0_beacon-chain.md#is_active_validator) can be used to check if a validator is active during a given shuffling epoch. Note that the `BeaconState` contains a field `current_shuffling_epoch` which dictates from which epoch the current active validators are taken. Usage is as follows: ```python shuffling_epoch = state.current_shuffling_epoch @@ -138,7 +138,7 @@ A validator has two primary responsibilities to the beacon chain -- [proposing b ### Block proposal -A validator is expected to propose a [`BeaconBlock`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#beaconblock) at the beginning of any slot during which `get_beacon_proposer_index(state, slot)` returns the validator's `validator_index`. To propose, the validator selects the `BeaconBlock`, `parent`, that in their view of the fork choice is the head of the chain during `slot - 1`. The validator is to create, sign, and broadcast a `block` that is a child of `parent` and that executes a valid [beacon chain state transition](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#beacon-chain-state-transition-function). +A validator is expected to propose a [`BeaconBlock`](../core/0_beacon-chain.md#beaconblock) at the beginning of any slot during which `get_beacon_proposer_index(state, slot)` returns the validator's `validator_index`. To propose, the validator selects the `BeaconBlock`, `parent`, that in their view of the fork choice is the head of the chain during `slot - 1`. The validator is to create, sign, and broadcast a `block` that is a child of `parent` and that executes a valid [beacon chain state transition](../core/0_beacon-chain.md#beacon-chain-state-transition-function). There is one proposer per slot, so if there are N active validators any individual validator will on average be assigned to propose once per N slots (eg. at 312500 validators = 10 million ETH, that's once per ~3 weeks). @@ -212,25 +212,25 @@ block_signature = bls_sign( ##### Proposer slashings -Up to `MAX_PROPOSER_SLASHINGS` [`ProposerSlashing`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#proposerslashing) objects can be included in the `block`. The proposer slashings must satisfy the verification conditions found in [proposer slashings processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#proposer-slashings). The validator receives a small "whistleblower" reward for each proposer slashing found and included. +Up to `MAX_PROPOSER_SLASHINGS` [`ProposerSlashing`](../core/0_beacon-chain.md#proposerslashing) objects can be included in the `block`. The proposer slashings must satisfy the verification conditions found in [proposer slashings processing](../core/0_beacon-chain.md#proposer-slashings). The validator receives a small "whistleblower" reward for each proposer slashing found and included. ##### Attester slashings -Up to `MAX_ATTESTER_SLASHINGS` [`AttesterSlashing`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attesterslashing) objects can be included in the `block`. The attester slashings must satisfy the verification conditions found in [Attester slashings processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attester-slashings). The validator receives a small "whistleblower" reward for each attester slashing found and included. +Up to `MAX_ATTESTER_SLASHINGS` [`AttesterSlashing`](../core/0_beacon-chain.md#attesterslashing) objects can be included in the `block`. The attester slashings must satisfy the verification conditions found in [Attester slashings processing](../core/0_beacon-chain.md#attester-slashings). The validator receives a small "whistleblower" reward for each attester slashing found and included. ##### Attestations -Up to `MAX_ATTESTATIONS` aggregate attestations can be included in the `block`. The attestations added must satisfy the verification conditions found in [attestation processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestations). To maximize profit, the validator should attempt to gather aggregate attestations that include singular attestations from the largest number of validators whose signatures from the same epoch have not previously been added on chain. +Up to `MAX_ATTESTATIONS` aggregate attestations can be included in the `block`. The attestations added must satisfy the verification conditions found in [attestation processing](../core/0_beacon-chain.md#attestations). To maximize profit, the validator should attempt to gather aggregate attestations that include singular attestations from the largest number of validators whose signatures from the same epoch have not previously been added on chain. ##### Deposits -If there are any unprocessed deposits for the existing `state.latest_eth1_data` (i.e. `state.latest_eth1_data.deposit_count > state.deposit_index`), then pending deposits _must_ be added to the block. The expected number of deposits is exactly `min(MAX_DEPOSITS, latest_eth1_data.deposit_count - state.deposit_index)`. These [`deposits`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#deposit) are constructed from the `Deposit` logs from the [Eth1.0 deposit contract](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#ethereum-10-deposit-contract) and must be processed in sequential order. The deposits included in the `block` must satisfy the verification conditions found in [deposits processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#deposits). +If there are any unprocessed deposits for the existing `state.latest_eth1_data` (i.e. `state.latest_eth1_data.deposit_count > state.deposit_index`), then pending deposits _must_ be added to the block. The expected number of deposits is exactly `min(MAX_DEPOSITS, latest_eth1_data.deposit_count - state.deposit_index)`. These [`deposits`](../core/0_beacon-chain.md#deposit) are constructed from the `Deposit` logs from the [Eth1.0 deposit contract](../core/0_beacon-chain.md#ethereum-10-deposit-contract) and must be processed in sequential order. The deposits included in the `block` must satisfy the verification conditions found in [deposits processing](../core/0_beacon-chain.md#deposits). The `proof` for each deposit must be constructed against the deposit root contained in `state.latest_eth1_data` rather than the deposit root at the time the deposit was initially logged from the 1.0 chain. This entails storing a full deposit merkle tree locally and computing updated proofs against the `latest_eth1_data.deposit_root` as needed. See [`minimal_merkle.py`](https://github.com/ethereum/research/blob/master/spec_pythonizer/utils/merkle_minimal.py) for a sample implementation. ##### Voluntary exits -Up to `MAX_VOLUNTARY_EXITS` [`VoluntaryExit`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#voluntaryexit) objects can be included in the `block`. The exits must satisfy the verification conditions found in [exits processing](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#voluntary-exits). +Up to `MAX_VOLUNTARY_EXITS` [`VoluntaryExit`](../core/0_beacon-chain.md#voluntaryexit) objects can be included in the `block`. The exits must satisfy the verification conditions found in [exits processing](../core/0_beacon-chain.md#voluntary-exits). ### Attestations @@ -240,7 +240,7 @@ A validator should create and broadcast the attestation halfway through the `slo #### Attestation data -First the validator should construct `attestation_data`, an [`AttestationData`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestationdata) object based upon the state at the assigned slot. +First the validator should construct `attestation_data`, an [`AttestationData`](../core/0_beacon-chain.md#attestationdata) object based upon the state at the assigned slot. * Let `head_block` be the result of running the fork choice during the assigned slot. * Let `head_state` be the state of `head_block` processed through any empty slots up to the assigned slot. @@ -285,7 +285,7 @@ Set `attestation_data.source_root = head_state.current_justified_root`. #### Construct attestation -Next the validator creates `attestation`, an [`Attestation`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestation) object. +Next the validator creates `attestation`, an [`Attestation`](../core/0_beacon-chain.md#attestation) object. ##### Data @@ -399,7 +399,7 @@ _Note_: Signed data must be within a sequential `Fork` context to conflict. Mess ### Proposer slashing -To avoid "proposer slashings", a validator must not sign two conflicting [`BeaconBlock`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#proposalsigneddata) where conflicting is defined as two distinct blocks within the same epoch. +To avoid "proposer slashings", a validator must not sign two conflicting [`BeaconBlock`](../core/0_beacon-chain.md#beaconblock) where conflicting is defined as two distinct blocks within the same epoch. _In phase 0, as long as the validator does not sign two different beacon blocks for the same epoch, the validator is safe against proposer slashings._ @@ -411,7 +411,7 @@ If the software crashes at some point within this routine, then when the validat ### Attester slashing -To avoid "attester slashings", a validator must not sign two conflicting [`AttestationData`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestationdata) objects where conflicting is defined as a set of two attestations that satisfy either [`is_double_vote`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#is_double_vote) or [`is_surround_vote`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#is_surround_vote). +To avoid "attester slashings", a validator must not sign two conflicting [`AttestationData`](../core/0_beacon-chain.md#attestationdata) objects where conflicting is defined as a set of two attestations that satisfy either [`is_double_vote`](../core/0_beacon-chain.md#is_double_vote) or [`is_surround_vote`](../core/0_beacon-chain.md#is_surround_vote). Specifically, when signing an `Attestation`, a validator should perform the following steps in the following order: 1. Save a record to hard disk that an attestation has been signed for source -- `attestation_data.source_epoch` -- and target -- `slot_to_epoch(attestation_data.slot)`. From ee1578d22a4dece09dc399716d8f3362ee8f708a Mon Sep 17 00:00:00 2001 From: JSON <49416440+JSON@users.noreply.github.com> Date: Fri, 12 Apr 2019 18:33:53 -0500 Subject: [PATCH 18/45] Update 0_beacon-chain.md (#908) --- specs/core/0_beacon-chain.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index d5e978afb..b7a63b4f6 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -284,7 +284,7 @@ These configurations are updated for releases, but may be out of sync during `de ## Data structures -The following data structures are defined as [SimpleSerialize (SSZ)](https://github.com/ethereum/eth2.0-specs/blob/master/specs/simple-serialize.md) objects. +The following data structures are defined as [SimpleSerialize (SSZ)](../simple-serialize.md) objects. The types are defined topologically to aid in facilitating an executable version of the spec. @@ -657,11 +657,11 @@ Note: We aim to migrate to a S[T/N]ARK-friendly hash function in a future Ethere ### `hash_tree_root` -`def hash_tree_root(object: SSZSerializable) -> Bytes32` is a function for hashing objects into a single root utilizing a hash tree structure. `hash_tree_root` is defined in the [SimpleSerialize spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/simple-serialize.md#tree-hash). +`def hash_tree_root(object: SSZSerializable) -> Bytes32` is a function for hashing objects into a single root utilizing a hash tree structure. `hash_tree_root` is defined in the [SimpleSerialize spec](../simple-serialize.md#merkleization). ### `signing_root` -`def signing_root(object: SSZContainer) -> Bytes32` is a function defined in the [SimpleSerialize spec](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#self-signed-containers) to compute signing messages. +`def signing_root(object: SSZContainer) -> Bytes32` is a function defined in the [SimpleSerialize spec](../simple-serialize.md#self-signed-containers) to compute signing messages. ### `get_temporary_block_header` @@ -1270,15 +1270,15 @@ def get_delayed_activation_exit_epoch(epoch: Epoch) -> Epoch: ### `bls_verify` -`bls_verify` is a function for verifying a BLS signature, defined in the [BLS Signature spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/bls_signature.md#bls_verify). +`bls_verify` is a function for verifying a BLS signature, defined in the [BLS Signature spec](../bls_signature.md#bls_verify). ### `bls_verify_multiple` -`bls_verify_multiple` is a function for verifying a BLS signature constructed from multiple messages, defined in the [BLS Signature spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/bls_signature.md#bls_verify_multiple). +`bls_verify_multiple` is a function for verifying a BLS signature constructed from multiple messages, defined in the [BLS Signature spec](../bls_signature.md#bls_verify_multiple). ### `bls_aggregate_pubkeys` -`bls_aggregate_pubkeys` is a function for aggregating multiple BLS public keys into a single aggregate key, defined in the [BLS Signature spec](https://github.com/ethereum/eth2.0-specs/blob/master/specs/bls_signature.md#bls_aggregate_pubkeys). +`bls_aggregate_pubkeys` is a function for aggregating multiple BLS public keys into a single aggregate key, defined in the [BLS Signature spec](../bls_signature.md#bls_aggregate_pubkeys). ### Routines for updating validator status From b34c41c5257731653d38fbb73373ec27996e138d Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sat, 13 Apr 2019 09:56:19 +1000 Subject: [PATCH 19/45] Update 0_beacon-chain.md (#911) Fix prose around state transition functionality --- 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 b7a63b4f6..a0001d03b 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1603,9 +1603,9 @@ We now define the state transition function. At a high level, the state transiti 4. The per-block transitions, which happens at every block. Transition section notes: -* The state caching caches the state root of the previous slot. +* The state caching caches the state root of the previous slot and updates block and state roots records. * The per-epoch transitions focus on the [validator](#dfn-validator) registry, including adjusting balances and activating and exiting [validators](#dfn-validator), as well as processing crosslinks and managing block justification/finalization. -* The per-slot transitions focus on the slot counter and block roots records updates. +* The per-slot transitions focus on the slot counter. * The per-block transitions generally focus on verifying aggregate signatures and saving temporary records relating to the per-block activity in the `BeaconState`. Beacon blocks that trigger unhandled Python exceptions (e.g. out-of-range list accesses) and failed `assert`s during the state transition are considered invalid. From 0a8b5275ab87c322206444e4ec19e77c20936ee6 Mon Sep 17 00:00:00 2001 From: JSON <49416440+JSON@users.noreply.github.com> Date: Fri, 12 Apr 2019 18:56:37 -0500 Subject: [PATCH 20/45] Update rpc-interface.md (#910) --- specs/networking/rpc-interface.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/networking/rpc-interface.md b/specs/networking/rpc-interface.md index 85ebe0bf6..5d408b5a0 100644 --- a/specs/networking/rpc-interface.md +++ b/specs/networking/rpc-interface.md @@ -26,7 +26,7 @@ Message body schemas are notated like this: Embedded types are serialized as SSZ Containers unless otherwise noted. -All referenced data structures can be found in the [0-beacon-chain](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/core/0_beacon-chain.md#data-structures) specification. +All referenced data structures can be found in the [0-beacon-chain](../core/0_beacon-chain.md#data-structures) specification. ## `libp2p` Protocol Names From 1932a4fbf4ef6cb0309bdff9722316710cc1ccc8 Mon Sep 17 00:00:00 2001 From: JSON <49416440+JSON@users.noreply.github.com> Date: Fri, 12 Apr 2019 19:46:22 -0500 Subject: [PATCH 21/45] Update 0_beacon-chain-validator.md --- specs/validator/0_beacon-chain-validator.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 1fbe08ef4..5f13fc2c3 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -100,11 +100,11 @@ In phase 0, all incoming validator deposits originate from the Ethereum 1.0 PoW To submit a deposit: -* Pack the validator's [initialization parameters](#initialization) into `deposit_input`, a [`DepositInput`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#depositinput) SSZ object. -* Let `proof_of_possession` be the result of `bls_sign` of the `signing_root(deposit_input)` with `domain=DOMAIN_DEPOSIT`. -* Set `deposit_input.proof_of_possession = proof_of_possession`. +* Pack the validator's [initialization parameters](#initialization) into `deposit_data`, a [`DepositData`](../core/0_beacon-chain.md#depositdata) SSZ object. +* Let `proof_of_possession` be the result of `bls_sign` of the `signing_root(deposit_data)` with `domain=DOMAIN_DEPOSIT`. +* Set `deposit_data.proof_of_possession = proof_of_possession`. * Let `amount` be the amount in Gwei to be deposited by the validator where `MIN_DEPOSIT_AMOUNT <= amount <= MAX_DEPOSIT_AMOUNT`. -* Send a transaction on the Ethereum 1.0 chain to `DEPOSIT_CONTRACT_ADDRESS` executing `deposit` along with `serialize(deposit_input)` as the singular `bytes` input along with a deposit `amount` in Gwei. +* Send a transaction on the Ethereum 1.0 chain to `DEPOSIT_CONTRACT_ADDRESS` executing `deposit` along with `serialize(deposit.data)` as the singular `bytes` input along with a deposit `amount` in Gwei. _Note_: Deposits made for the same `pubkey` are treated as for the same validator. A singular `Validator` will be added to `state.validator_registry` with each additional deposit amount added to the validator's balance. A validator can only be activated when total deposits for the validator pubkey meet or exceed `MAX_DEPOSIT_AMOUNT`. From 710bacad757ae91fd94577b513b335e748b33e03 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Sat, 13 Apr 2019 11:56:55 +1000 Subject: [PATCH 22/45] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index a0001d03b..f6427f1d4 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -234,7 +234,7 @@ These configurations are updated for releases, but may be out of sync during `de | `SLOTS_PER_HISTORICAL_ROOT` | `2**13` (= 8,192) | slots | ~13 hours | | `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours | | `PERSISTENT_COMMITTEE_PERIOD` | `2**11` (= 2,048) | epochs | 9 days | -| `MAX_CROSSLINK_EPOCHS` | `2**6` (= 64) | +| `MAX_CROSSLINK_EPOCHS` | `2**6` (= 64) | epochs | ~7 hours | * `MAX_CROSSLINK_EPOCHS` should be a small constant times `SHARD_COUNT // SLOTS_PER_EPOCH` From 75df6106aae2ed3ce1cb51894f5e78a6c589c664 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 12 Apr 2019 23:23:04 -0600 Subject: [PATCH 23/45] clean up some deposit notes in validator doc --- specs/validator/0_beacon-chain-validator.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specs/validator/0_beacon-chain-validator.md b/specs/validator/0_beacon-chain-validator.md index 5f13fc2c3..60d283664 100644 --- a/specs/validator/0_beacon-chain-validator.md +++ b/specs/validator/0_beacon-chain-validator.md @@ -104,7 +104,8 @@ To submit a deposit: * Let `proof_of_possession` be the result of `bls_sign` of the `signing_root(deposit_data)` with `domain=DOMAIN_DEPOSIT`. * Set `deposit_data.proof_of_possession = proof_of_possession`. * Let `amount` be the amount in Gwei to be deposited by the validator where `MIN_DEPOSIT_AMOUNT <= amount <= MAX_DEPOSIT_AMOUNT`. -* Send a transaction on the Ethereum 1.0 chain to `DEPOSIT_CONTRACT_ADDRESS` executing `deposit` along with `serialize(deposit.data)` as the singular `bytes` input along with a deposit `amount` in Gwei. +* Set `deposit_data.amount = amount`. +* Send a transaction on the Ethereum 1.0 chain to `DEPOSIT_CONTRACT_ADDRESS` executing `deposit(deposit_input: bytes[512])` along with `serialize(deposit_data)` as the singular `bytes` input along with a deposit of `amount` Gwei. _Note_: Deposits made for the same `pubkey` are treated as for the same validator. A singular `Validator` will be added to `state.validator_registry` with each additional deposit amount added to the validator's balance. A validator can only be activated when total deposits for the validator pubkey meet or exceed `MAX_DEPOSIT_AMOUNT`. From 2fceb36f145fe8590bdab0fd15379f5f82f06842 Mon Sep 17 00:00:00 2001 From: JSON <49416440+JSON@users.noreply.github.com> Date: Sat, 13 Apr 2019 17:01:33 -0500 Subject: [PATCH 24/45] Update README.md --- specs/test_formats/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/test_formats/README.md b/specs/test_formats/README.md index 2c1ef2d03..3271f55f8 100644 --- a/specs/test_formats/README.md +++ b/specs/test_formats/README.md @@ -84,7 +84,7 @@ The aim is to provide clients with a well-defined scope of work to run a particu - Clients that are complete are expected to contribute to testing, seeking for better resources to get conformance with the spec, and other clients. - Clients that are not complete in functionality can choose to ignore suites that use certain test-runners, or specific handlers of these test-runners. -- Clients that are on older versions can test there work based on older releases of the generated tests, and catch up with newer releases when possible. +- Clients that are on older versions can test their work based on older releases of the generated tests, and catch up with newer releases when possible. ## Test Suite From f7c5b0a1c615bc71bda71b71ed6ab5a5ac38b0b5 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 14 Apr 2019 08:30:13 +1000 Subject: [PATCH 25/45] set activation_eligibility_epoch during process_deposit --- specs/core/0_beacon-chain.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index bdce2e348..67eb7b000 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1462,7 +1462,7 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], # Randomness and committees latest_randao_mixes=Vector([ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH)]), latest_start_shard=GENESIS_START_SHARD, - + # Exit queue exit_epoch=GENESIS_EPOCH, exit_queue_filled=0, @@ -2271,6 +2271,7 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: validator = Validator( pubkey=pubkey, withdrawal_credentials=deposit.data.withdrawal_credentials, + activation_eligibility_epoch=FAR_FUTURE_EPOCH, activation_epoch=FAR_FUTURE_EPOCH, exit_epoch=FAR_FUTURE_EPOCH, withdrawable_epoch=FAR_FUTURE_EPOCH, From 37004404d04e0e6d4d7eec95e7097e844fa40106 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 14 Apr 2019 09:13:53 +1000 Subject: [PATCH 26/45] add exit queue test --- specs/core/0_beacon-chain.md | 29 ++-- .../block_processing/test_process_deposit.py | 4 +- .../block_processing/test_voluntary_exit.py | 155 ++++++++++-------- 3 files changed, 106 insertions(+), 82 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 67eb7b000..40363a666 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1311,23 +1311,26 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: Note that this function mutates ``state``. """ validator = state.validator_registry[index] + # Operation is a no-op if validator is already in the queue - if validator.exit_epoch == FAR_FUTURE_EPOCH: - # Update exit queue counters - delayed_activation_exit_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) - if state.exit_epoch < delayed_activation_exit_epoch: - state.exit_epoch = delayed_activation_exit_epoch + if validator.exit_epoch != FAR_FUTURE_EPOCH: + return - if state.exit_queue_filled >= MAX_EXITS_PER_EPOCH: - state.exit_epoch += 1 - state.exit_queue_filled = 0 + # Update exit queue counters + delayed_activation_exit_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) + if state.exit_epoch < delayed_activation_exit_epoch: + state.exit_epoch = delayed_activation_exit_epoch - # Set validator exit epoch and withdrawable epoch - validator.exit_epoch = state.exit_epoch - validator.withdrawable_epoch = validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY + if state.exit_queue_filled >= MAX_EXITS_PER_EPOCH: + state.exit_epoch += 1 + state.exit_queue_filled = 0 - # Extend queue - state.exit_queue_filled += 1 + # Set validator exit epoch and withdrawable epoch + validator.exit_epoch = state.exit_epoch + validator.withdrawable_epoch = validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY + + # Extend queue + state.exit_queue_filled += 1 ``` #### `slash_validator` diff --git a/tests/phase0/block_processing/test_process_deposit.py b/tests/phase0/block_processing/test_process_deposit.py index 0726dddef..0c3447d4e 100644 --- a/tests/phase0/block_processing/test_process_deposit.py +++ b/tests/phase0/block_processing/test_process_deposit.py @@ -15,8 +15,8 @@ from tests.phase0.helpers import ( ) -# mark entire file as 'voluntary_exits' -pytestmark = pytest.mark.voluntary_exits +# mark entire file as 'deposits' +pytestmark = pytest.mark.deposits def test_success(state): diff --git a/tests/phase0/block_processing/test_voluntary_exit.py b/tests/phase0/block_processing/test_voluntary_exit.py index 7627f1f0b..2f0693454 100644 --- a/tests/phase0/block_processing/test_voluntary_exit.py +++ b/tests/phase0/block_processing/test_voluntary_exit.py @@ -18,124 +18,145 @@ from tests.phase0.helpers import ( pytestmark = pytest.mark.voluntary_exits -def test_success(state): - pre_state = deepcopy(state) - # - # 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 +def run_voluntary_exit_processing(state, voluntary_exit, valid=True): + """ + Run ``process_voluntary_exit`` returning the pre and post state. + If ``valid == False``, run expecting ``AssertionError`` + """ + post_state = deepcopy(state) - # - # build voluntary exit - # - current_epoch = get_current_epoch(pre_state) - validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0] - privkey = pubkey_to_privkey[pre_state.validator_registry[validator_index].pubkey] + if not valid: + with pytest.raises(AssertionError): + process_voluntary_exit(post_state, voluntary_exit) + return state, None + + process_voluntary_exit(post_state, voluntary_exit) + + validator_index = voluntary_exit.validator_index + assert state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH + assert post_state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH + + return state, post_state + + +def test_success(state): + # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit + state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH + + current_epoch = get_current_epoch(state) + validator_index = get_active_validator_indices(state.validator_registry, current_epoch)[0] + privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] voluntary_exit = build_voluntary_exit( - pre_state, + state, current_epoch, validator_index, privkey, ) - post_state = deepcopy(pre_state) + pre_state, post_state = run_voluntary_exit_processing(state, voluntary_exit) + return pre_state, voluntary_exit, post_state - # - # test valid exit - # - process_voluntary_exit(post_state, voluntary_exit) - assert pre_state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH - assert post_state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH +def test_success_exit_queue(state): + # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow for exit + state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH + + current_epoch = get_current_epoch(state) + + # exit `MAX_EXITS_PER_EPOCH` + initial_indices = get_active_validator_indices(state.validator_registry,current_epoch)[:spec.MAX_EXITS_PER_EPOCH] + post_state = state + for index in initial_indices: + privkey = pubkey_to_privkey[state.validator_registry[index].pubkey] + voluntary_exit = build_voluntary_exit( + state, + current_epoch, + index, + privkey, + ) + + _, post_state = run_voluntary_exit_processing(post_state, voluntary_exit) + + # exit an additional validator + validator_index = get_active_validator_indices(state.validator_registry,current_epoch)[-1] + privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] + voluntary_exit = build_voluntary_exit( + state, + current_epoch, + validator_index, + privkey, + ) + + pre_state, post_state = run_voluntary_exit_processing(post_state, voluntary_exit) + + assert ( + post_state.validator_registry[validator_index].exit_epoch == + post_state.validator_registry[initial_indices[0]].exit_epoch + 1 + ) return pre_state, voluntary_exit, post_state def test_validator_not_active(state): - pre_state = deepcopy(state) - current_epoch = get_current_epoch(pre_state) - validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0] - privkey = pubkey_to_privkey[pre_state.validator_registry[validator_index].pubkey] + current_epoch = get_current_epoch(state) + validator_index = get_active_validator_indices(state.validator_registry, current_epoch)[0] + privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] - # - # setup pre_state - # - pre_state.validator_registry[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH + state.validator_registry[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH # # build and test voluntary exit # voluntary_exit = build_voluntary_exit( - pre_state, + state, current_epoch, validator_index, privkey, ) - with pytest.raises(AssertionError): - process_voluntary_exit(pre_state, voluntary_exit) - - return pre_state, voluntary_exit, None + pre_state, post_state = run_voluntary_exit_processing(state, voluntary_exit, False) + return pre_state, voluntary_exit, post_state def test_validator_already_exited(state): - pre_state = deepcopy(state) - # - # setup pre_state - # # move state forward PERSISTENT_COMMITTEE_PERIOD epochs to allow validator able to exit - pre_state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH + state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH - current_epoch = get_current_epoch(pre_state) - validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0] - privkey = pubkey_to_privkey[pre_state.validator_registry[validator_index].pubkey] + current_epoch = get_current_epoch(state) + validator_index = get_active_validator_indices(state.validator_registry, current_epoch)[0] + privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] # but validator already has exited - pre_state.validator_registry[validator_index].exit_epoch = current_epoch + 2 + state.validator_registry[validator_index].exit_epoch = current_epoch + 2 - # - # build voluntary exit - # voluntary_exit = build_voluntary_exit( - pre_state, + state, current_epoch, validator_index, privkey, ) - with pytest.raises(AssertionError): - process_voluntary_exit(pre_state, voluntary_exit) - - return pre_state, voluntary_exit, None + pre_state, post_state = run_voluntary_exit_processing(state, voluntary_exit, False) + return pre_state, voluntary_exit, post_state def test_validator_not_active_long_enough(state): - pre_state = deepcopy(state) - # - # setup pre_state - # - current_epoch = get_current_epoch(pre_state) - validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0] - privkey = pubkey_to_privkey[pre_state.validator_registry[validator_index].pubkey] + current_epoch = get_current_epoch(state) + validator_index = get_active_validator_indices(state.validator_registry, current_epoch)[0] + privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] - # - # build voluntary exit - # voluntary_exit = build_voluntary_exit( - pre_state, + state, current_epoch, validator_index, privkey, ) assert ( - current_epoch - pre_state.validator_registry[validator_index].activation_epoch < + current_epoch - state.validator_registry[validator_index].activation_epoch < spec.PERSISTENT_COMMITTEE_PERIOD ) - with pytest.raises(AssertionError): - process_voluntary_exit(pre_state, voluntary_exit) - - return pre_state, voluntary_exit, None + pre_state, post_state = run_voluntary_exit_processing(state, voluntary_exit, False) + return pre_state, voluntary_exit, post_state From bade9ff3edcd3529a2456b6a62a4fc4102b928c8 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 14 Apr 2019 09:21:29 +1000 Subject: [PATCH 27/45] enhance exit queue test --- tests/phase0/block_processing/test_voluntary_exit.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/phase0/block_processing/test_voluntary_exit.py b/tests/phase0/block_processing/test_voluntary_exit.py index 2f0693454..bddf874de 100644 --- a/tests/phase0/block_processing/test_voluntary_exit.py +++ b/tests/phase0/block_processing/test_voluntary_exit.py @@ -76,7 +76,11 @@ def test_success_exit_queue(state): privkey, ) - _, post_state = run_voluntary_exit_processing(post_state, voluntary_exit) + pre_state, post_state = run_voluntary_exit_processing(post_state, voluntary_exit) + assert post_state.exit_queue_filled > pre_state.exit_queue_filled + assert post_state.exit_epoch >= pre_state.exit_epoch + + assert post_state.exit_epoch == pre_state.exit_epoch # exit an additional validator validator_index = get_active_validator_indices(state.validator_registry,current_epoch)[-1] @@ -94,6 +98,9 @@ def test_success_exit_queue(state): post_state.validator_registry[validator_index].exit_epoch == post_state.validator_registry[initial_indices[0]].exit_epoch + 1 ) + assert post_state.exit_queue_filled == 1 + assert post_state.exit_epoch == pre_state.exit_epoch + 1 + return pre_state, voluntary_exit, post_state From 5466a4875d894e2ca9ca9d8ee60d63026be5062c Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sun, 14 Apr 2019 09:54:35 +1000 Subject: [PATCH 28/45] Update 1_shard-data-chains.md Fix some typos --- specs/core/1_shard-data-chains.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index dc990567d..6a2094688 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -317,8 +317,8 @@ def is_valid_shard_block(beacon_blocks: List[BeaconBlock], assert len(block.attestations) <= MAX_SHARD_ATTESTIONS for _, attestation in enumerate(block.attestations): assert max(GENESIS_SHARD_SLOT, block.slot - SLOTS_PER_EPOCH) <= attestation.data.slot - assert attesation.data.slot <= block.slot - MIN_ATTESTATION_INCLUSION_DELAY - assert attetation.data.shart == block.shard + assert attestation.data.slot <= block.slot - MIN_ATTESTATION_INCLUSION_DELAY + assert attestation.data.shard == block.shard verify_shard_attestation_signature(beacon_state, attestation) # Check signature From f85e7ac44737b76899c1757689febd56775d9060 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sat, 13 Apr 2019 22:14:05 -0500 Subject: [PATCH 29/45] Added churn limit logic --- specs/core/0_beacon-chain.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 40363a666..6d0d84e12 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -93,6 +93,7 @@ - [`is_surround_vote`](#is_surround_vote) - [`integer_squareroot`](#integer_squareroot) - [`get_delayed_activation_exit_epoch`](#get_delayed_activation_exit_epoch) + - [`get_churn_limit`](#get_churn_limit) - [`bls_verify`](#bls_verify) - [`bls_verify_multiple`](#bls_verify_multiple) - [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys) @@ -182,8 +183,7 @@ These configurations are updated for releases, but may be out of sync during `de | `SHARD_COUNT` | `2**10` (= 1,024) | | `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) | | `MAX_ATTESTATION_PARTICIPANTS` | `2**12` (= 4,096) | -| `MAX_EXITS_PER_EPOCH` | `2**2` (= 4) | -| `MAX_ACTIVATIONS_PER_FINALIZED_EPOCH` | `2**2` (= 4) | +| `MIN_PER_EPOCH_CHURN_LIMIT` | `2**2` (= 4) | | `SHUFFLE_ROUND_COUNT` | 90 | * For the safety of crosslinks `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `SLOTS_PER_EPOCH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes of at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.) @@ -233,6 +233,7 @@ These configurations are updated for releases, but may be out of sync during `de | `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours | | `PERSISTENT_COMMITTEE_PERIOD` | `2**11` (= 2,048) | epochs | 9 days | | `MAX_CROSSLINK_EPOCHS` | `2**6` (= 64) | epochs | ~7 hours | +| `MAX_FULL_CHURN_EPOCHS` | `2**22` (= 4,194,304) | epochs | ~9 months | * `MAX_CROSSLINK_EPOCHS` should be a small constant times `SHARD_COUNT // SLOTS_PER_EPOCH` @@ -1269,6 +1270,15 @@ def get_delayed_activation_exit_epoch(epoch: Epoch) -> Epoch: return epoch + 1 + ACTIVATION_EXIT_DELAY ``` +### `get_churn_limit` + +```python +def get_churn_limit(state: BeaconState) -> int: + return max( + MIN_PER_EPOCH_CHURN_LIMIT, + MAX_FULL_CHURN_EPOCHS // len(get_active_validators(state, get_current_epoch(state))) + ) + ### `bls_verify` `bls_verify` is a function for verifying a BLS signature, defined in the [BLS Signature spec](../bls_signature.md#bls_verify). @@ -1321,7 +1331,7 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: if state.exit_epoch < delayed_activation_exit_epoch: state.exit_epoch = delayed_activation_exit_epoch - if state.exit_queue_filled >= MAX_EXITS_PER_EPOCH: + if state.exit_queue_filled >= get_churn_limit(state): state.exit_epoch += 1 state.exit_queue_filled = 0 @@ -1962,7 +1972,7 @@ def update_registry(state: BeaconState) -> None: validator.activation_epoch >= get_delayed_activation_exit_epoch(state.finalized_epoch) ], key=lambda index: state.validator_registry[index].activation_eligibility_epoch) - for index in activation_queue[:MAX_ACTIVATIONS_PER_FINALIZED_EPOCH]: + for index in activation_queue[:get_churn_limit(state)]: activate_validator(state, index, is_genesis=False) state.latest_start_shard = ( From 0d6448303d916888c2ba43e1ff0d0358c5829bc0 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 14 Apr 2019 16:49:17 +1000 Subject: [PATCH 30/45] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 1 + 1 file changed, 1 insertion(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 6d0d84e12..c86e25ea9 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1278,6 +1278,7 @@ def get_churn_limit(state: BeaconState) -> int: MIN_PER_EPOCH_CHURN_LIMIT, MAX_FULL_CHURN_EPOCHS // len(get_active_validators(state, get_current_epoch(state))) ) +``` ### `bls_verify` From d01fb80fd4a5f704269aeedb6cbca0d90971cb4d Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 14 Apr 2019 17:02:04 +1000 Subject: [PATCH 31/45] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index c86e25ea9..dbce308ad 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -595,8 +595,8 @@ The types are defined topologically to aid in facilitating an executable version 'latest_start_shard': 'uint64', # Exit queue - 'exit_epoch': 'uint64', - 'exit_queue_filled': 'uint64', + 'exit_queue_epoch': 'uint64', + 'exit_queue_churn': 'uint64', # Finality 'previous_epoch_attestations': [PendingAttestation], @@ -1329,19 +1329,20 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: # Update exit queue counters delayed_activation_exit_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) - if state.exit_epoch < delayed_activation_exit_epoch: - state.exit_epoch = delayed_activation_exit_epoch + if state.exit_queue_epoch < delayed_activation_exit_epoch: + state.exit_queue_epoch = delayed_activation_exit_epoch + state.exit_queue_churn = 0 - if state.exit_queue_filled >= get_churn_limit(state): - state.exit_epoch += 1 - state.exit_queue_filled = 0 + state.exit_queue_churn += 1 + if state.exit_queue_churn > get_churn_limit(state): + state.exit_queue_epoch += 1 + state.exit_queue_churn = 0 # Set validator exit epoch and withdrawable epoch - validator.exit_epoch = state.exit_epoch + validator.exit_epoch = state.exit_queue_epoch validator.withdrawable_epoch = validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY # Extend queue - state.exit_queue_filled += 1 ``` #### `slash_validator` @@ -1478,8 +1479,8 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], latest_start_shard=GENESIS_START_SHARD, # Exit queue - exit_epoch=GENESIS_EPOCH, - exit_queue_filled=0, + exit_queue_epoch=GENESIS_EPOCH, + exit_queue_churn=0, # Finality previous_epoch_attestations=[], From 15bb9676d5b3dd4cace31058916f071aef8cfcad Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 14 Apr 2019 17:04:36 +1000 Subject: [PATCH 32/45] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index dbce308ad..78aade7cb 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1276,7 +1276,7 @@ def get_delayed_activation_exit_epoch(epoch: Epoch) -> Epoch: def get_churn_limit(state: BeaconState) -> int: return max( MIN_PER_EPOCH_CHURN_LIMIT, - MAX_FULL_CHURN_EPOCHS // len(get_active_validators(state, get_current_epoch(state))) + MAX_FULL_CHURN_EPOCHS // len(get_active_validator_indices(state, get_current_epoch(state))) ) ``` From 7705ecf89cd240152576527bf290b5a3daeea0d9 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 14 Apr 2019 17:28:45 +1000 Subject: [PATCH 33/45] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 78aade7cb..6ec3ca739 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -184,6 +184,7 @@ These configurations are updated for releases, but may be out of sync during `de | `TARGET_COMMITTEE_SIZE` | `2**7` (= 128) | | `MAX_ATTESTATION_PARTICIPANTS` | `2**12` (= 4,096) | | `MIN_PER_EPOCH_CHURN_LIMIT` | `2**2` (= 4) | +| `CHURN_LIMIT_QUOTIENT` | `2**16` (= 65,536) | | `SHUFFLE_ROUND_COUNT` | 90 | * For the safety of crosslinks `TARGET_COMMITTEE_SIZE` exceeds [the recommended minimum committee size of 111](https://vitalik.ca/files/Ithaca201807_Sharding.pdf); with sufficient active validators (at least `SLOTS_PER_EPOCH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes of at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.) @@ -237,7 +238,6 @@ These configurations are updated for releases, but may be out of sync during `de * `MAX_CROSSLINK_EPOCHS` should be a small constant times `SHARD_COUNT // SLOTS_PER_EPOCH` - ### State list lengths | Name | Value | Unit | Duration | @@ -746,11 +746,11 @@ def is_slashable_validator(validator: Validator, epoch: Epoch) -> bool: ### `get_active_validator_indices` ```python -def get_active_validator_indices(validators: List[Validator], epoch: Epoch) -> List[ValidatorIndex]: +def get_active_validator_indices(state: BeaconState, epoch: Epoch) -> List[ValidatorIndex]: """ - Get indices of active validators from ``validators``. + Get active validator indices at ``epoch``. """ - return [i for i, v in enumerate(validators) if is_active_validator(v, epoch)] + return [i for i, v in enumerate(state.validator_registry) if is_active_validator(v, epoch)] ``` ### `get_balance` @@ -844,7 +844,7 @@ def get_epoch_committee_count(state: BeaconState, epoch: Epoch) -> int: """ Return the number of committees in one epoch. """ - active_validators = get_active_validator_indices(state.validator_registry, epoch) + active_validators = get_active_validator_indices(state, epoch) return max( 1, min( @@ -896,10 +896,7 @@ def get_crosslink_committees_at_slot(state: BeaconState, next_epoch = current_epoch + 1 assert previous_epoch <= epoch <= next_epoch - indices = get_active_validator_indices( - state.validator_registry, - epoch, - ) + indices = get_active_validator_indices(state, epoch) if epoch == current_epoch: start_shard = state.latest_start_shard @@ -1276,7 +1273,7 @@ def get_delayed_activation_exit_epoch(epoch: Epoch) -> Epoch: def get_churn_limit(state: BeaconState) -> int: return max( MIN_PER_EPOCH_CHURN_LIMIT, - MAX_FULL_CHURN_EPOCHS // len(get_active_validator_indices(state, get_current_epoch(state))) + len(get_active_validator_indices(state, get_current_epoch(state))) // CHURN_LIMIT_QUOTIENT ) ``` @@ -1517,7 +1514,7 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], if get_effective_balance(state, validator_index) >= MAX_DEPOSIT_AMOUNT: activate_validator(state, validator_index, is_genesis=True) - genesis_active_index_root = hash_tree_root(get_active_validator_indices(state.validator_registry, GENESIS_EPOCH)) + genesis_active_index_root = hash_tree_root(get_active_validator_indices(state, GENESIS_EPOCH)) for index in range(LATEST_ACTIVE_INDEX_ROOTS_LENGTH): state.latest_active_index_roots[index] = genesis_active_index_root @@ -1651,12 +1648,12 @@ We define some helper functions utilized when processing an epoch transition: ```python def get_current_total_balance(state: BeaconState) -> Gwei: - return get_total_balance(state, get_active_validator_indices(state.validator_registry, get_current_epoch(state))) + return get_total_balance(state, get_active_validator_indices(state, get_current_epoch(state))) ``` ```python def get_previous_total_balance(state: BeaconState) -> Gwei: - return get_total_balance(state, get_active_validator_indices(state.validator_registry, get_previous_epoch(state))) + return get_total_balance(state, get_active_validator_indices(state, get_previous_epoch(state))) ``` ```python @@ -1994,7 +1991,7 @@ def process_slashings(state: BeaconState) -> None: Note that this function mutates ``state``. """ current_epoch = get_current_epoch(state) - active_validator_indices = get_active_validator_indices(state.validator_registry, current_epoch) + active_validator_indices = get_active_validator_indices(state, current_epoch) total_balance = get_total_balance(state, active_validator_indices) # Compute `total_penalties` @@ -2022,7 +2019,7 @@ def finish_epoch_update(state: BeaconState) -> None: # Set active index root index_root_position = (next_epoch + ACTIVATION_EXIT_DELAY) % LATEST_ACTIVE_INDEX_ROOTS_LENGTH state.latest_active_index_roots[index_root_position] = hash_tree_root( - get_active_validator_indices(state.validator_registry, next_epoch + ACTIVATION_EXIT_DELAY) + get_active_validator_indices(state, next_epoch + ACTIVATION_EXIT_DELAY) ) # Set total slashed balances state.latest_slashed_balances[next_epoch % LATEST_SLASHED_EXIT_LENGTH] = ( From da4a1430eaaa6df49a3231ca94b515abeba1dec8 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 14 Apr 2019 17:52:50 +1000 Subject: [PATCH 34/45] fix test --- .../block_processing/test_voluntary_exit.py | 23 ++++++++++--------- tests/phase0/helpers.py | 2 +- tests/phase0/test_sanity.py | 14 +++++------ 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/tests/phase0/block_processing/test_voluntary_exit.py b/tests/phase0/block_processing/test_voluntary_exit.py index bddf874de..b8af85a97 100644 --- a/tests/phase0/block_processing/test_voluntary_exit.py +++ b/tests/phase0/block_processing/test_voluntary_exit.py @@ -5,6 +5,7 @@ import build.phase0.spec as spec from build.phase0.spec import ( get_active_validator_indices, + get_churn_limit, get_current_epoch, process_voluntary_exit, ) @@ -44,7 +45,7 @@ def test_success(state): state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH current_epoch = get_current_epoch(state) - validator_index = get_active_validator_indices(state.validator_registry, current_epoch)[0] + validator_index = get_active_validator_indices(state, current_epoch)[0] privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] voluntary_exit = build_voluntary_exit( @@ -65,7 +66,7 @@ def test_success_exit_queue(state): current_epoch = get_current_epoch(state) # exit `MAX_EXITS_PER_EPOCH` - initial_indices = get_active_validator_indices(state.validator_registry,current_epoch)[:spec.MAX_EXITS_PER_EPOCH] + initial_indices = get_active_validator_indices(state, current_epoch)[:get_churn_limit(state)] post_state = state for index in initial_indices: privkey = pubkey_to_privkey[state.validator_registry[index].pubkey] @@ -77,13 +78,13 @@ def test_success_exit_queue(state): ) pre_state, post_state = run_voluntary_exit_processing(post_state, voluntary_exit) - assert post_state.exit_queue_filled > pre_state.exit_queue_filled - assert post_state.exit_epoch >= pre_state.exit_epoch + assert post_state.exit_queue_churn > pre_state.exit_queue_churn + assert post_state.exit_queue_epoch >= pre_state.exit_queue_epoch - assert post_state.exit_epoch == pre_state.exit_epoch + assert post_state.exit_queue_epoch == pre_state.exit_queue_epoch # exit an additional validator - validator_index = get_active_validator_indices(state.validator_registry,current_epoch)[-1] + validator_index = get_active_validator_indices(state, current_epoch)[-1] privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] voluntary_exit = build_voluntary_exit( state, @@ -98,8 +99,8 @@ def test_success_exit_queue(state): post_state.validator_registry[validator_index].exit_epoch == post_state.validator_registry[initial_indices[0]].exit_epoch + 1 ) - assert post_state.exit_queue_filled == 1 - assert post_state.exit_epoch == pre_state.exit_epoch + 1 + assert post_state.exit_queue_churn == 0 + assert post_state.exit_queue_epoch == pre_state.exit_queue_epoch + 1 return pre_state, voluntary_exit, post_state @@ -107,7 +108,7 @@ def test_success_exit_queue(state): def test_validator_not_active(state): current_epoch = get_current_epoch(state) - validator_index = get_active_validator_indices(state.validator_registry, current_epoch)[0] + validator_index = get_active_validator_indices(state, current_epoch)[0] privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] state.validator_registry[validator_index].activation_epoch = spec.FAR_FUTURE_EPOCH @@ -131,7 +132,7 @@ def test_validator_already_exited(state): state.slot += spec.PERSISTENT_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH current_epoch = get_current_epoch(state) - validator_index = get_active_validator_indices(state.validator_registry, current_epoch)[0] + validator_index = get_active_validator_indices(state, current_epoch)[0] privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] # but validator already has exited @@ -150,7 +151,7 @@ def test_validator_already_exited(state): def test_validator_not_active_long_enough(state): current_epoch = get_current_epoch(state) - validator_index = get_active_validator_indices(state.validator_registry, current_epoch)[0] + validator_index = get_active_validator_indices(state, current_epoch)[0] privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] voluntary_exit = build_voluntary_exit( diff --git a/tests/phase0/helpers.py b/tests/phase0/helpers.py index 5fe22e6a4..8c8064fc1 100644 --- a/tests/phase0/helpers.py +++ b/tests/phase0/helpers.py @@ -199,7 +199,7 @@ def build_deposit(state, def get_valid_proposer_slashing(state): current_epoch = get_current_epoch(state) - validator_index = get_active_validator_indices(state.validator_registry, current_epoch)[-1] + validator_index = get_active_validator_indices(state, current_epoch)[-1] privkey = pubkey_to_privkey[state.validator_registry[validator_index].pubkey] slot = state.slot diff --git a/tests/phase0/test_sanity.py b/tests/phase0/test_sanity.py index 9b2bf9c7f..f9e62620c 100644 --- a/tests/phase0/test_sanity.py +++ b/tests/phase0/test_sanity.py @@ -278,7 +278,7 @@ def test_attestation(state): def test_voluntary_exit(state): pre_state = deepcopy(state) validator_index = get_active_validator_indices( - pre_state.validator_registry, + pre_state, get_current_epoch(pre_state) )[-1] @@ -326,7 +326,7 @@ def test_voluntary_exit(state): def test_no_exit_churn_too_long_since_change(state): pre_state = deepcopy(state) validator_index = get_active_validator_indices( - pre_state.validator_registry, + pre_state, get_current_epoch(pre_state) )[-1] @@ -346,8 +346,8 @@ def test_no_exit_churn_too_long_since_change(state): state_transition(post_state, block) assert post_state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH - assert post_state.exit_queue_filled == pre_state.exit_queue_filled - assert post_state.exit_epoch == pre_state.exit_epoch + assert post_state.exit_queue_churn == pre_state.exit_queue_churn + assert post_state.exit_queue_epoch == pre_state.exit_queue_epoch return pre_state, [block], post_state @@ -355,8 +355,8 @@ def test_no_exit_churn_too_long_since_change(state): def test_transfer(state): pre_state = deepcopy(state) current_epoch = get_current_epoch(pre_state) - sender_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[-1] - recipient_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[0] + sender_index = get_active_validator_indices(pre_state, current_epoch)[-1] + recipient_index = get_active_validator_indices(pre_state, current_epoch)[0] transfer_pubkey = pubkeys[-1] transfer_privkey = privkeys[-1] amount = get_balance(pre_state, sender_index) @@ -407,7 +407,7 @@ def test_balance_driven_status_transitions(state): pre_state = deepcopy(state) current_epoch = get_current_epoch(pre_state) - validator_index = get_active_validator_indices(pre_state.validator_registry, current_epoch)[-1] + validator_index = get_active_validator_indices(pre_state, current_epoch)[-1] assert pre_state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH From 229af3dedacfd5a7abf6f005cd59e35d5f20d338 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 14 Apr 2019 18:10:44 +1000 Subject: [PATCH 35/45] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 6ec3ca739..3f92e8b6f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -594,10 +594,6 @@ The types are defined topologically to aid in facilitating an executable version 'latest_randao_mixes': ['bytes32', LATEST_RANDAO_MIXES_LENGTH], 'latest_start_shard': 'uint64', - # Exit queue - 'exit_queue_epoch': 'uint64', - 'exit_queue_churn': 'uint64', - # Finality 'previous_epoch_attestations': [PendingAttestation], 'current_epoch_attestations': [PendingAttestation], @@ -1324,19 +1320,21 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: if validator.exit_epoch != FAR_FUTURE_EPOCH: return - # Update exit queue counters - delayed_activation_exit_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) - if state.exit_queue_epoch < delayed_activation_exit_epoch: - state.exit_queue_epoch = delayed_activation_exit_epoch - state.exit_queue_churn = 0 + # Compute exit queue parameters + exit_queue_epoch = sorted([validator.exit_epoch for validator in state.validator_registry if + validator.exit_epoch != FAR_FUTURE_EPOCH + ].append(GENESIS_EPOCH), key=lambda index: state.validator_registry[index].exit_epoch)[-1] - state.exit_queue_churn += 1 - if state.exit_queue_churn > get_churn_limit(state): - state.exit_queue_epoch += 1 - state.exit_queue_churn = 0 + delayed_activation_exit_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) + if exit_queue_epoch < delayed_activation_exit_epoch: + exit_queue_epoch = delayed_activation_exit_epoch + + exit_queue_churn = len([v for v in state.validator_registry if v.exit_epoch == exit_queue_epoch]) + if exit_queue_churn > get_churn_limit(state): + exit_queue_epoch += 1 # Set validator exit epoch and withdrawable epoch - validator.exit_epoch = state.exit_queue_epoch + validator.exit_epoch = exit_queue_epoch validator.withdrawable_epoch = validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY # Extend queue @@ -1475,10 +1473,6 @@ def get_genesis_beacon_state(genesis_validator_deposits: List[Deposit], latest_randao_mixes=Vector([ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH)]), latest_start_shard=GENESIS_START_SHARD, - # Exit queue - exit_queue_epoch=GENESIS_EPOCH, - exit_queue_churn=0, - # Finality previous_epoch_attestations=[], current_epoch_attestations=[], From 0b770121fec097611397802a1674c81602517678 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 14 Apr 2019 18:23:30 +1000 Subject: [PATCH 36/45] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 3f92e8b6f..0531f9807 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -234,7 +234,6 @@ These configurations are updated for releases, but may be out of sync during `de | `MIN_VALIDATOR_WITHDRAWABILITY_DELAY` | `2**8` (= 256) | epochs | ~27 hours | | `PERSISTENT_COMMITTEE_PERIOD` | `2**11` (= 2,048) | epochs | 9 days | | `MAX_CROSSLINK_EPOCHS` | `2**6` (= 64) | epochs | ~7 hours | -| `MAX_FULL_CHURN_EPOCHS` | `2**22` (= 4,194,304) | epochs | ~9 months | * `MAX_CROSSLINK_EPOCHS` should be a small constant times `SHARD_COUNT // SLOTS_PER_EPOCH` From 06807cf5201e1facf8438bf5cdaa76ec0cdb8ff4 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 14 Apr 2019 18:50:05 +1000 Subject: [PATCH 37/45] fix tests and off by one error --- specs/core/0_beacon-chain.md | 11 ++++++----- tests/phase0/block_processing/test_voluntary_exit.py | 7 ------- tests/phase0/test_sanity.py | 2 -- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 3f92e8b6f..352fccf76 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1320,17 +1320,18 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: if validator.exit_epoch != FAR_FUTURE_EPOCH: return - # Compute exit queue parameters - exit_queue_epoch = sorted([validator.exit_epoch for validator in state.validator_registry if - validator.exit_epoch != FAR_FUTURE_EPOCH - ].append(GENESIS_EPOCH), key=lambda index: state.validator_registry[index].exit_epoch)[-1] + # Compute exit queue parameters (pad with GENESIS_EPOCH in case empty) + exit_queue_epoch = sorted([ + validator.exit_epoch for validator in state.validator_registry + if validator.exit_epoch != FAR_FUTURE_EPOCH + ] + [GENESIS_EPOCH])[-1] delayed_activation_exit_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) if exit_queue_epoch < delayed_activation_exit_epoch: exit_queue_epoch = delayed_activation_exit_epoch exit_queue_churn = len([v for v in state.validator_registry if v.exit_epoch == exit_queue_epoch]) - if exit_queue_churn > get_churn_limit(state): + if exit_queue_churn >= get_churn_limit(state): exit_queue_epoch += 1 # Set validator exit epoch and withdrawable epoch diff --git a/tests/phase0/block_processing/test_voluntary_exit.py b/tests/phase0/block_processing/test_voluntary_exit.py index b8af85a97..eb01c2a8a 100644 --- a/tests/phase0/block_processing/test_voluntary_exit.py +++ b/tests/phase0/block_processing/test_voluntary_exit.py @@ -78,10 +78,6 @@ def test_success_exit_queue(state): ) pre_state, post_state = run_voluntary_exit_processing(post_state, voluntary_exit) - assert post_state.exit_queue_churn > pre_state.exit_queue_churn - assert post_state.exit_queue_epoch >= pre_state.exit_queue_epoch - - assert post_state.exit_queue_epoch == pre_state.exit_queue_epoch # exit an additional validator validator_index = get_active_validator_indices(state, current_epoch)[-1] @@ -99,9 +95,6 @@ def test_success_exit_queue(state): post_state.validator_registry[validator_index].exit_epoch == post_state.validator_registry[initial_indices[0]].exit_epoch + 1 ) - assert post_state.exit_queue_churn == 0 - assert post_state.exit_queue_epoch == pre_state.exit_queue_epoch + 1 - return pre_state, voluntary_exit, post_state diff --git a/tests/phase0/test_sanity.py b/tests/phase0/test_sanity.py index f9e62620c..08c7610c0 100644 --- a/tests/phase0/test_sanity.py +++ b/tests/phase0/test_sanity.py @@ -346,8 +346,6 @@ def test_no_exit_churn_too_long_since_change(state): state_transition(post_state, block) assert post_state.validator_registry[validator_index].exit_epoch == spec.FAR_FUTURE_EPOCH - assert post_state.exit_queue_churn == pre_state.exit_queue_churn - assert post_state.exit_queue_epoch == pre_state.exit_queue_epoch return pre_state, [block], post_state From 0908ffa653a5a9569e1e8798add8c8c81500f3a1 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 14 Apr 2019 19:01:53 +1000 Subject: [PATCH 38/45] Update 0_beacon-chain.md --- specs/core/0_beacon-chain.md | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index cd8e1a427..ec12e4000 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1313,22 +1313,15 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: Initiate the validator of the given ``index``. Note that this function mutates ``state``. """ + # Return if validator already initiated exit validator = state.validator_registry[index] - - # Operation is a no-op if validator is already in the queue if validator.exit_epoch != FAR_FUTURE_EPOCH: return - # Compute exit queue parameters (pad with GENESIS_EPOCH in case empty) - exit_queue_epoch = sorted([ - validator.exit_epoch for validator in state.validator_registry - if validator.exit_epoch != FAR_FUTURE_EPOCH - ] + [GENESIS_EPOCH])[-1] - - delayed_activation_exit_epoch = get_delayed_activation_exit_epoch(get_current_epoch(state)) - if exit_queue_epoch < delayed_activation_exit_epoch: - exit_queue_epoch = delayed_activation_exit_epoch - + # Compute exit queue epoch + exit_epochs = [v.exit_epoch for v in state.validator_registry if v.exit_epoch != FAR_FUTURE_EPOCH] + latest_exit_epoch = GENESIS_EPOCH if len(exit_epochs) == 0 else sorted(exit_epochs)[-1] + exit_queue_epoch = max(latest_exit_epoch, get_delayed_activation_exit_epoch(get_current_epoch(state))) exit_queue_churn = len([v for v in state.validator_registry if v.exit_epoch == exit_queue_epoch]) if exit_queue_churn >= get_churn_limit(state): exit_queue_epoch += 1 @@ -1336,8 +1329,6 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: # Set validator exit epoch and withdrawable epoch validator.exit_epoch = exit_queue_epoch validator.withdrawable_epoch = validator.exit_epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY - - # Extend queue ``` #### `slash_validator` From 875b2ba00dfd0b0b5eccd7d0bfb2833a9fd6c0d2 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 14 Apr 2019 19:11:40 +1000 Subject: [PATCH 39/45] 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 ec12e4000..97abdbee7 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1320,8 +1320,8 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: # Compute exit queue epoch exit_epochs = [v.exit_epoch for v in state.validator_registry if v.exit_epoch != FAR_FUTURE_EPOCH] - latest_exit_epoch = GENESIS_EPOCH if len(exit_epochs) == 0 else sorted(exit_epochs)[-1] - exit_queue_epoch = max(latest_exit_epoch, get_delayed_activation_exit_epoch(get_current_epoch(state))) + exit_epochs += [get_delayed_activation_exit_epoch(get_current_epoch(state))] + exit_queue_epoch = sorted(exit_epochs)[-1] exit_queue_churn = len([v for v in state.validator_registry if v.exit_epoch == exit_queue_epoch]) if exit_queue_churn >= get_churn_limit(state): exit_queue_epoch += 1 From 3394368a6674241be42b7fa65cac4d41868a4908 Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 14 Apr 2019 19:14:27 +1000 Subject: [PATCH 40/45] 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 97abdbee7..78da35ea3 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1320,8 +1320,7 @@ def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None: # Compute exit queue epoch exit_epochs = [v.exit_epoch for v in state.validator_registry if v.exit_epoch != FAR_FUTURE_EPOCH] - exit_epochs += [get_delayed_activation_exit_epoch(get_current_epoch(state))] - exit_queue_epoch = sorted(exit_epochs)[-1] + exit_queue_epoch = sorted(exit_epochs + [get_delayed_activation_exit_epoch(get_current_epoch(state))])[-1] exit_queue_churn = len([v for v in state.validator_registry if v.exit_epoch == exit_queue_epoch]) if exit_queue_churn >= get_churn_limit(state): exit_queue_epoch += 1 From be86f966f87958856584b3f20c095abf910a3d0c Mon Sep 17 00:00:00 2001 From: Diederik Loerakker Date: Sun, 14 Apr 2019 19:18:00 +1000 Subject: [PATCH 41/45] fix transfer invariant, credits to @holiman for finding the edge case (#916) --- specs/core/0_beacon-chain.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index f6427f1d4..df9ace80b 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2416,6 +2416,8 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None: get_balance(state, transfer.sender) == transfer.amount + transfer.fee or get_balance(state, transfer.sender) >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT ) + # No self-transfers (to enforce >= MIN_DEPOSIT_AMOUNT or zero balance invariant) + assert transfer.sender != transfer.recipient # A transfer is valid in only one slot assert state.slot == transfer.slot # Only withdrawn or not-yet-deposited accounts can transfer From a376b6607fe5e6406371f44254960e891ee5ee8d Mon Sep 17 00:00:00 2001 From: Justin Date: Sun, 14 Apr 2019 21:53:32 +1000 Subject: [PATCH 42/45] Cleaner dust checking in transfers Inspired by [this](https://github.com/ethereum/eth2.0-specs/pull/916#commitcomment-33170877). --- specs/core/0_beacon-chain.md | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 649254f44..5a205e562 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2334,14 +2334,6 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None: """ # Verify the amount and fee aren't individually too big (for anti-overflow purposes) assert get_balance(state, transfer.sender) >= max(transfer.amount, transfer.fee) - # Verify that we have enough ETH to send, and that after the transfer the balance will be either - # exactly zero or at least MIN_DEPOSIT_AMOUNT - assert ( - get_balance(state, transfer.sender) == transfer.amount + transfer.fee or - get_balance(state, transfer.sender) >= transfer.amount + transfer.fee + MIN_DEPOSIT_AMOUNT - ) - # No self-transfers (to enforce >= MIN_DEPOSIT_AMOUNT or zero balance invariant) - assert transfer.sender != transfer.recipient # A transfer is valid in only one slot assert state.slot == transfer.slot # Only withdrawn or not-yet-deposited accounts can transfer @@ -2365,6 +2357,9 @@ def process_transfer(state: BeaconState, transfer: Transfer) -> None: decrease_balance(state, transfer.sender, transfer.amount + transfer.fee) increase_balance(state, transfer.recipient, transfer.amount) increase_balance(state, get_beacon_proposer_index(state, state.slot), transfer.fee) + # Verify balances are not dust + assert not (0 < get_balance(state, transfer.sender) < MIN_DEPOSIT_AMOUNT) + assert not (0 < get_balance(state, transfer.recipient) < MIN_DEPOSIT_AMOUNT) ``` #### State root verification From bcf10ecf11fcf137cb5753dbef5092502e1158b8 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sun, 14 Apr 2019 22:55:38 +1000 Subject: [PATCH 43/45] Update 0_beacon-chain.md (#921) Fix typo to set the right property on the correct object --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 649254f44..d4548f05a 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1937,7 +1937,7 @@ def process_balance_driven_status_transitions(state: BeaconState) -> None: for index, validator in enumerate(state.validator_registry): balance = get_balance(state, index) if validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and balance >= MAX_DEPOSIT_AMOUNT: - state.activation_eligibility_epoch = get_current_epoch(state) + validator.activation_eligibility_epoch = get_current_epoch(state) if is_active_validator(validator, get_current_epoch(state)) and balance < EJECTION_BALANCE: initiate_validator_exit(state, index) From c01fb1eea69f64f9e33f23dacdd74c5bb61da343 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sun, 14 Apr 2019 22:56:01 +1000 Subject: [PATCH 44/45] Update 0_beacon-chain.md (#922) More clean up on recent switch to exit queue --- specs/core/0_beacon-chain.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index d4548f05a..aa3726294 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -2271,7 +2271,6 @@ def process_deposit(state: BeaconState, deposit: Deposit) -> None: activation_epoch=FAR_FUTURE_EPOCH, exit_epoch=FAR_FUTURE_EPOCH, withdrawable_epoch=FAR_FUTURE_EPOCH, - initiated_exit=False, slashed=False, high_balance=0 ) From 40d6a2635af9603f96132dac9072ac35192a4857 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Mon, 15 Apr 2019 07:03:47 +1000 Subject: [PATCH 45/45] Update 0_beacon-chain.md (#923) Fix another typo w/ the withdrawal ~> exit queue PR --- specs/core/0_beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 4e697139e..2d150837e 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1950,7 +1950,7 @@ Run the following function: ```python def update_registry(state: BeaconState) -> None: activation_queue = sorted([ - validator for validator in state.validator_registry if + index for index, validator in enumerate(state.validator_registry) if validator.activation_eligibility_epoch != FAR_FUTURE_EPOCH and validator.activation_epoch >= get_delayed_activation_exit_epoch(state.finalized_epoch) ], key=lambda index: state.validator_registry[index].activation_eligibility_epoch)