From 10a8ba2d9e66957a17c805ff26315ae2d4575f1f Mon Sep 17 00:00:00 2001 From: terence tsao Date: Fri, 28 Dec 2018 09:04:00 -0800 Subject: [PATCH 01/36] rewording for validator registry precondition --- 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 045a914b0..6e054853b 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1726,7 +1726,7 @@ def process_ejections(state: BeaconState) -> None: If the following are satisfied: * `state.finalized_slot > state.validator_registry_latest_change_slot` -* `state.latest_crosslinks[shard].slot > state.validator_registry_latest_change_slot` for every shard number `shard` in `state.shard_committees_at_slots` +* `state.latest_crosslinks[shard].slot > state.validator_registry_latest_change_slot` for every shard number `shard` in `shard_committee` from `shard_committee_at_slot` in `state.shard_committees_at_slots` update the validator registry and associated fields by running From d9e897bbcbc281dfc298b5396c0c6d8c8d962713 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 28 Dec 2018 12:04:03 -0600 Subject: [PATCH 02/36] Status code / slot reorganization * Added `activation_slot`, `exit_slot`, `penalized_slot`, `withdrawal_slot`, use these to determine if a validator is active * Universal min activation/exit delay of 256 slots * Min exit time of ~1 day, but penalization delays this to ~18 days * Penalty calculation period of `[time penalized - 18 days, time penalized + 18 days]`; made the total penalties array fixed size and wraparound to make calculation more fine-grained * Processes withdrawals in all epochs, not just dynasty-changing epochs * Change `get_shuffling` function to take slot as argument Not yet done: * Removed `shard_committees` from the state * Removed persistent committees from the state --- specs/core/0_beacon-chain.md | 237 +++++++++++++++++++---------------- 1 file changed, 131 insertions(+), 106 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 045a914b0..fcc521181 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -66,7 +66,7 @@ - [`get_active_validator_indices`](#get_active_validator_indices) - [`shuffle`](#shuffle) - [`split`](#split) - - [`get_new_shuffling`](#get_new_shuffling) + - [`get_shuffling`](#get_shuffling) - [`get_shard_committees_at_slot`](#get_shard_committees_at_slot) - [`get_block_root`](#get_block_root) - [`get_beacon_proposer_index`](#get_beacon_proposer_index) @@ -168,6 +168,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | `MAX_CASPER_VOTES` | `2**10` (= 1,024) | votes | | `LATEST_BLOCK_ROOTS_LENGTH` | `2**13` (= 8,192) | block roots | | `LATEST_RANDAO_MIXES_LENGTH` | `2**13` (= 8,192) | randao mixes | +| `LATEST_PENALIZED_EXIT_LENGTH` | `2**13` (= 8,192) | cycles | ~36 days | | `EMPTY_SIGNATURE` | `[bytes48(0), bytes48(0)]` | - | * 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 `EPOCH_LENGTH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.) @@ -188,6 +189,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | `INITIAL_FORK_VERSION` | `0` | | `INITIAL_SLOT_NUMBER` | `0` | | `ZERO_HASH` | `bytes([0] * 32)` | +| `FAR_FUTURE_SLOT` | `2**63` | ### Time parameters @@ -196,9 +198,10 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | `SLOT_DURATION` | `6` | seconds | 6 seconds | | `MIN_ATTESTATION_INCLUSION_DELAY` | `2**2` (= 4) | slots | 24 seconds | | `EPOCH_LENGTH` | `2**6` (= 64) | slots | 6.4 minutes | +| `SEED_LOOKAHEAD` | `2**6` (= 64) | slots | 6.4 minutes | +| `ENTRY_EXIT_DELAY` | `2**8` (= 256) | slots | 25.6 minutes | | `POW_RECEIPT_ROOT_VOTING_PERIOD` | `2**10` (= 1,024) | slots | ~1.7 hours | | `SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD` | `2**17` (= 131,072) | slots | ~9 days | -| `COLLECTIVE_PENALTY_CALCULATION_PERIOD` | `2**20` (= 1,048,576) | slots | ~73 days | | `ZERO_BALANCE_VALIDATOR_TTL` | `2**22` (= 4,194,304) | slots | ~291 days | ### Reward and penalty quotients @@ -213,15 +216,13 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted * The `BASE_REWARD_QUOTIENT` parameter dictates the per-epoch reward. It corresponds to ~2.54% annual interest assuming 10 million participating ETH in every epoch. * The `INACTIVITY_PENALTY_QUOTIENT` equals `INVERSE_SQRT_E_DROP_TIME**2` where `INVERSE_SQRT_E_DROP_TIME := 2**12 epochs` (~18 days) is the time it takes the inactivity penalty to reduce the balance of non-participating [validators](#dfn-validator) to about `1/sqrt(e) ~= 60.6%`. Indeed, the balance retained by offline [validators](#dfn-validator) after `n` epochs is about `(1-1/INACTIVITY_PENALTY_QUOTIENT)**(n**2/2)` so after `INVERSE_SQRT_E_DROP_TIME` epochs it is roughly `(1-1/INACTIVITY_PENALTY_QUOTIENT)**(INACTIVITY_PENALTY_QUOTIENT/2) ~= 1/sqrt(e)`. -### Status codes +### Status flags | Name | Value | | - | - | -| `PENDING_ACTIVATION` | `0` | -| `ACTIVE` | `1` | -| `ACTIVE_PENDING_EXIT` | `2` | -| `EXITED_WITHOUT_PENALTY` | `3` | -| `EXITED_WITH_PENALTY` | `4` | +| `INITIATED_EXIT` | `2**0` (= 1) | +| `WITHDRAWABLE` | `2**1` (= 2) | + ### Max operations per block @@ -521,12 +522,18 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted 'randao_commitment': 'hash32', # Slots the proposer has skipped (i.e. layers of RANDAO expected) 'randao_layers': 'uint64', - # Status code - 'status': 'uint64', - # Slot when validator last changed status (or 0) - 'latest_status_change_slot': 'uint64', - # Exit counter when validator exited (or 0) + # Slot when validator activated + 'activation_slot': 'uint64', + # Slot when validator exited + 'exit_slot': 'uint64', + # Slot when validator withdrew + 'withdrawal_slot': 'uint64', + # Slot when validator was penalized + 'penalized_slot': 'uint64', + # Exit counter when validator exited 'exit_count': 'uint64', + # Status flags + 'status_flags': 'uint64', # Proof of custody commitment 'poc_commitment': 'hash32', # Slot the proof of custody seed was last changed @@ -618,6 +625,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted latest_registry_delta_root: 'hash32', validator_index: 'uint24', pubkey: 'uint384', + slot: 'uint64', flag: 'uint64', } ``` @@ -738,7 +746,7 @@ The beacon chain fork choice rule is a hybrid that combines justification and fi def lmd_ghost(store, start): validators = start.state.validator_registry active_validators = [validators[i] for i in - get_active_validator_indices(validators)] + get_active_validator_indices(validators, start.state.slot)] attestation_targets = [get_latest_attestation_target(store, validator) for validator in active_validators] def get_vote_count(block): @@ -778,21 +786,21 @@ Note: We aim to migrate to a S[T/N]ARK-friendly hash function in a future Ethere #### `is_active_validator` ```python -def is_active_validator(validator: ValidatorRecord) -> bool: +def is_active_validator(validator: ValidatorRecord, slot: int) -> bool: """ Checks if ``validator`` is active. """ - return validator.status in [ACTIVE, ACTIVE_PENDING_EXIT] + return validator.activation_slot <= slot < validator.exit_slot ``` #### `get_active_validator_indices` ```python -def get_active_validator_indices(validators: [ValidatorRecord]) -> List[int]: +def get_active_validator_indices(validators: [ValidatorRecord], slot: int) -> List[int]: """ Gets indices of active validators from ``validators``. """ - return [i for i, v in enumerate(validators) if is_active_validator(v)] + return [i for i, v in enumerate(validators) if is_active_validator(v, slot)] ``` #### `shuffle` @@ -862,16 +870,17 @@ def split(values: List[Any], split_count: int) -> List[Any]: ] ``` -#### `get_new_shuffling` +#### `get_shuffling` ```python -def get_new_shuffling(seed: Hash32, - validators: List[ValidatorRecord], - crosslinking_start_shard: int) -> List[List[ShardCommittee]]: +def get_shuffling(seed: Hash32, + validators: List[ValidatorRecord], + crosslinking_start_shard: int, + slot: int) -> List[List[ShardCommittee]]: """ Shuffles ``validators`` into shard committees using ``seed`` as entropy. """ - active_validator_indices = get_active_validator_indices(validators) + active_validator_indices = get_active_validator_indices(validators, slot) committees_per_slot = max( 1, @@ -907,6 +916,8 @@ def get_new_shuffling(seed: Hash32, return output ``` +**Invariant**: if `get_shuffling(seed, validators, shard, slot)` returns some value `x`, it should return the same value `x` for the same `seed` and `shard` and possible future modifications of `validators` forever in phase 0, and until the ~1 year deletion delay in phase 1 and in the future. + Here's a diagram of what is going on: ![](http://vitalik.ca/files/ShuffleAndAssign.png?1) @@ -1008,6 +1019,7 @@ def get_effective_balance(state: State, index: int) -> int: def get_new_validator_registry_delta_chain_tip(current_validator_registry_delta_chain_tip: Hash32, validator_index: int, pubkey: int, + slot: int, flag: int) -> Hash32: """ Compute the next root in the validator registry delta chain. @@ -1017,6 +1029,7 @@ def get_new_validator_registry_delta_chain_tip(current_validator_registry_delta_ latest_registry_delta_root=current_validator_registry_delta_chain_tip, validator_index=validator_index, pubkey=pubkey, + slot=slot, flag=flag, ) ) @@ -1196,7 +1209,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], # Recent state latest_crosslinks=[CrosslinkRecord(slot=INITIAL_SLOT_NUMBER, shard_block_root=ZERO_HASH) for _ in range(SHARD_COUNT)], latest_block_roots=[ZERO_HASH for _ in range(LATEST_BLOCK_ROOTS_LENGTH)], - latest_penalized_exit_balances=[], + latest_penalized_exit_balances=[0 for _ in LATEST_PENALIZED_EXIT_LENGTH], latest_attestations=[], batched_block_roots=[], @@ -1217,14 +1230,14 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], poc_commitment=deposit.deposit_data.deposit_input.poc_commitment, ) if get_effective_balance(state, validator_index) == MAX_DEPOSIT * GWEI_PER_ETH: - update_validator_status(state, validator_index, ACTIVE) + activate_validator(state, validator_index) # set initial committee shuffling - initial_shuffling = get_new_shuffling(ZERO_HASH, state.validator_registry, 0) + initial_shuffling = get_shuffling(ZERO_HASH, state.validator_registry, 0, INITIAL_SLOT_NUMBER) state.shard_committees_at_slots = initial_shuffling + initial_shuffling # set initial persistent shuffling - active_validator_indices = get_active_validator_indices(state.validator_registry) + active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot) state.persistent_committees = split(shuffle(active_validator_indices, ZERO_HASH), SHARD_COUNT) return state @@ -1238,8 +1251,8 @@ First, two helper functions: def min_empty_validator_index(validators: List[ValidatorRecord], validator_balances: List[int], current_slot: int) -> int: - for i, (v, vbal) in enumerate(zip(validators, validator_balances)): - if vbal == 0 and v.latest_status_change_slot + ZERO_BALANCE_VALIDATOR_TTL <= current_slot: + for i, v in enumerate(validators): + if v.withdrawal_slot + ZERO_BALANCE_VALIDATOR_TTL <= current_slot: return i return None ``` @@ -1304,8 +1317,10 @@ def process_deposit(state: BeaconState, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, randao_layers=0, - status=PENDING_ACTIVATION, - latest_status_change_slot=state.slot, + activation_slot=FAR_FUTURE_SLOT, + exit_slot=FAR_FUTURE_SLOT, + withdrawal_slot=FAR_FUTURE_SLOT, + penalized_slot=FAR_FUTURE_SLOT, exit_count=0, poc_commitment=poc_commitment, last_poc_change_slot=0, @@ -1330,26 +1345,7 @@ def process_deposit(state: BeaconState, return index ``` -### Routine for updating validator status - -```python -def update_validator_status(state: BeaconState, - index: int, - new_status: int) -> None: - """ - Update the validator status with the given ``index`` to ``new_status``. - Handle other general accounting related to this status update. - Note that this function mutates ``state``. - """ - if new_status == ACTIVE: - activate_validator(state, index) - if new_status == ACTIVE_PENDING_EXIT: - initiate_validator_exit(state, index) - if new_status in [EXITED_WITH_PENALTY, EXITED_WITHOUT_PENALTY]: - exit_validator(state, index, new_status) -``` - -The following are helpers and should only be called via `update_validator_status`: +### Routines for updating validator status ```python def activate_validator(state: BeaconState, @@ -1359,15 +1355,15 @@ def activate_validator(state: BeaconState, Note that this function mutates ``state``. """ validator = state.validator_registry[index] - if validator.status != PENDING_ACTIVATION: + if validator.activation_slot <= state.slot: return - validator.status = ACTIVE - validator.latest_status_change_slot = state.slot + validator.activation_slot = state.slot + ENTRY_EXIT_DELAY state.validator_registry_delta_chain_tip = get_new_validator_registry_delta_chain_tip( current_validator_registry_delta_chain_tip=state.validator_registry_delta_chain_tip, validator_index=index, pubkey=validator.pubkey, + slot=validator.activation_slot, flag=ACTIVATION, ) ``` @@ -1380,40 +1376,22 @@ def initiate_validator_exit(state: BeaconState, Note that this function mutates ``state``. """ validator = state.validator_registry[index] - if validator.status != ACTIVE: - return - - validator.status = ACTIVE_PENDING_EXIT - validator.latest_status_change_slot = state.slot + validator.status_flags |= INITIATED_EXIT ``` ```python def exit_validator(state: BeaconState, - index: int, - new_status: int) -> None: + index: int) -> None: """ Exit the validator with the given ``index``. Note that this function mutates ``state``. """ validator = state.validator_registry[index] - prev_status = validator.status - if prev_status == EXITED_WITH_PENALTY: - return - - validator.status = new_status - validator.latest_status_change_slot = state.slot - - if new_status == EXITED_WITH_PENALTY: - state.latest_penalized_exit_balances[state.slot // COLLECTIVE_PENALTY_CALCULATION_PERIOD] += get_effective_balance(state, index) - - whistleblower_index = get_beacon_proposer_index(state, state.slot) - whistleblower_reward = get_effective_balance(state, index) // WHISTLEBLOWER_REWARD_QUOTIENT - state.validator_balances[whistleblower_index] += whistleblower_reward - state.validator_balances[index] -= whistleblower_reward - - if prev_status == EXITED_WITHOUT_PENALTY: + if validator.exit_slot < state.slot + ENTRY_EXIT_DELAY: return + + validator.exit_slot = state.slot + ENTRY_EXIT_DELAY # The following updates only occur if not previous exited state.validator_registry_exit_count += 1 @@ -1422,8 +1400,10 @@ def exit_validator(state: BeaconState, current_validator_registry_delta_chain_tip=state.validator_registry_delta_chain_tip, validator_index=index, pubkey=validator.pubkey, + slot=validator.exit_slot, flag=EXIT, ) + state.validator_registry_exit_count += 1 # Remove validator from persistent committees for committee in state.persistent_committees: @@ -1433,6 +1413,27 @@ def exit_validator(state: BeaconState, break ``` +```python +def penalize_validator(state: BeaconState, + index: int) -> None: + + exit_validator(state, index) + state.latest_penalized_exit_balances[(state.slot // EPOCH_LENGTH) % LATEST_PENALIZED_EXIT_LENGTH] += get_effective_balance(state, index) + + whistleblower_index = get_beacon_proposer_index(state, state.slot) + whistleblower_reward = get_effective_balance(state, index) // WHISTLEBLOWER_REWARD_QUOTIENT + state.validator_balances[whistleblower_index] += whistleblower_reward + state.validator_balances[index] -= whistleblower_reward + state.validator_registry[index].penalized_slot = state.slot +``` + +```python +def set_validator_withdrawable(state: BeaconState, + index: int) -> None: + + state.validator_registry[index].status_flags |= WITHDRAWABLE +``` + ## Per-slot processing Below are the processing steps that happen at every slot. @@ -1492,7 +1493,7 @@ For each `proposer_slashing` in `block.body.proposer_slashings`: * Verify that `proposer.status != EXITED_WITH_PENALTY`. * Verify that `bls_verify(pubkey=proposer.pubkey, message=hash_tree_root(proposer_slashing.proposal_data_1), signature=proposer_slashing.proposal_signature_1, domain=get_domain(state.fork_data, proposer_slashing.proposal_data_1.slot, DOMAIN_PROPOSAL))`. * Verify that `bls_verify(pubkey=proposer.pubkey, message=hash_tree_root(proposer_slashing.proposal_data_2), signature=proposer_slashing.proposal_signature_2, domain=get_domain(state.fork_data, proposer_slashing.proposal_data_2.slot, DOMAIN_PROPOSAL))`. -* Run `update_validator_status(state, proposer_slashing.proposer_index, new_status=EXITED_WITH_PENALTY)`. +* Run `penalize_validator(state, proposer_slashing.proposer_index)`. #### Casper slashings @@ -1509,7 +1510,7 @@ For each `casper_slashing` in `block.body.casper_slashings`: * Verify that `is_double_vote(slashable_vote_data_1.data, slashable_vote_data_2.data)` or `is_surround_vote(slashable_vote_data_1.data, slashable_vote_data_2.data)`. * Verify that `verify_slashable_vote_data(state, slashable_vote_data_1)`. * Verify that `verify_slashable_vote_data(state, slashable_vote_data_2)`. -* For each [validator](#dfn-validator) index `i` in `intersection`, if `state.validator_registry[i].status` does not equal `EXITED_WITH_PENALTY`, then run `update_validator_status(state, i, new_status=EXITED_WITH_PENALTY)` +* For each [validator](#dfn-validator) index `i` in `intersection`, if `state.validator_registry[i].status` does not equal `EXITED_WITH_PENALTY`, then run `penalize_validator(state, i)`. #### Attestations @@ -1573,11 +1574,10 @@ Verify that `len(block.body.exits) <= MAX_EXITS`. For each `exit` in `block.body.exits`: * Let `validator = state.validator_registry[exit.validator_index]`. -* Verify that `validator.status == ACTIVE`. +* Verify that `validator.exit_slot > state.slot + ENTRY_EXIT_DELAY`. * Verify that `state.slot >= exit.slot`. -* Verify that `state.slot >= validator.latest_status_change_slot + SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD`. * Verify that `bls_verify(pubkey=validator.pubkey, message=ZERO_HASH, signature=exit.signature, domain=get_domain(state.fork_data, exit.slot, DOMAIN_EXIT))`. -* Run `update_validator_status(state, validator_index, new_status=ACTIVE_PENDING_EXIT)`. +* Run `initiate_validator_exit(state, validator_index)`. #### Miscellaneous @@ -1591,7 +1591,7 @@ The steps below happen when `state.slot % EPOCH_LENGTH == 0`. All [validators](#dfn-validator): -* Let `active_validator_indices = get_active_validator_indices(state.validator_registry)`. +* Let `active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot)`. * Let `total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices])`. [Validators](#dfn-Validator) attesting during the current epoch: @@ -1718,7 +1718,7 @@ def process_ejections(state: BeaconState) -> None: """ for index in active_validator_indices(state.validator_registry): if state.validator_balances[index] < EJECTION_BALANCE: - update_validator_status(state, index, new_status=EXITED_WITHOUT_PENALTY) + exit_validator(state, index) ``` ### Validator registry @@ -1737,7 +1737,7 @@ def update_validator_registry(state: BeaconState) -> None: Note that this function mutates ``state``. """ # The active validators - active_validator_indices = get_active_validator_indices(state.validator_registry) + active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot) # The total effective balance of active validators total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices]) @@ -1750,65 +1750,89 @@ def update_validator_registry(state: BeaconState) -> None: # Activate validators within the allowable balance churn balance_churn = 0 for index, validator in enumerate(state.validator_registry): - if validator.status == PENDING_ACTIVATION and state.validator_balances[index] >= MAX_DEPOSIT * GWEI_PER_ETH: + if validator.activation_slot > state.slot and state.validator_balances[index] >= MAX_DEPOSIT * GWEI_PER_ETH: # 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 - update_validator_status(state, index, new_status=ACTIVE) + activate_validator(state, index) # Exit validators within the allowable balance churn balance_churn = 0 for index, validator in enumerate(state.validator_registry): - if validator.status == ACTIVE_PENDING_EXIT: + if validator.exit_slot > state.slot + ENTRY_EXIT_DELAY and validator.status_flags & 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 - update_validator_status(state, index, new_status=EXITED_WITHOUT_PENALTY) + exit_validator(state, index) +``` +Regardless of whether the above conditions are satisfied, run the following: - # Calculate the total ETH that has been penalized in the last ~2-3 withdrawal periods - period_index = current_slot // COLLECTIVE_PENALTY_CALCULATION_PERIOD - total_penalties = ( - (latest_penalized_exit_balances[period_index]) + - (latest_penalized_exit_balances[period_index - 1] if period_index >= 1 else 0) + - (latest_penalized_exit_balances[period_index - 2] if period_index >= 2 else 0) - ) +```python +def process_penalties_and_exits(state: BeaconState) -> None: - # Calculate penalties for slashed validators - def to_penalize(index): - return state.validator_registry[index].status == EXITED_WITH_PENALTY - validators_to_penalize = filter(to_penalize, range(len(validator_registry))) - for index in validators_to_penalize: - state.validator_balances[index] -= get_effective_balance(state, index) * min(total_penalties * 3, total_balance) // total_balance + # The active validators + active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot) + # The total effective balance of active validators + total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices]) - return validator_registry, latest_penalized_exit_balances, validator_registry_delta_chain_tip + for i, validator in enumerate(validators): + if (state.slot // EPOCH_LENGTH) - (validator.penalized_slot // EPOCH_LENGTH) == LATEST_PENALIZED_EXIT_LENGTH // 2: + e = (state.slot // EPOCH_LENGTH) % LATEST_PENALIZED_EXIT_LENGTH + total_at_start = state.latest_penalized_exit_balances[(e+1)%LATEST_PENALIZED_EXIT_LENGTH] + total_at_end = state.latest_penalized_exit_balances[e] + total_penalties = total_at_end - total_at_start + penalty = get_effective_balance(state, i) * min(total_penalties * 3, total_balance) // total_balance + state.validator_balances[index] -= penalty + + def eligible(index): + validator = state.validator_registry[index] + if validator.penalized_slot <= state.slot: + PENALIZED_WITHDRAWAL_TIME = LATEST_PENALIZED_EXIT_LENGTH * EPOCH_LENGTH // 2 + return state.slot >= validator.penalized_slot + PENALIZED_WITHDRAWAL_TIME + else: + return state.slot >= validator.exit_slot + MIN_VALIDATOR_WITHDRAWAL_TIME + + eligible_indices = filter(eligible, all_indices) + sorted_indices = sorted(eligible_indices, filter=lambda index: state.validator_registry[index].exit_count) + withdrawn_so_far = 0 + for index in sorted_indices: + validator = state.validator_registry[index] + if validator.status == EXITED_WITH_PENALTY: + # TODO: calculate and apply penalties for slashed validators + penalty = 1 + state.validator_balances[index] -= penalty + set_validator_withdrawable(state, index) + withdrawn_so_far += 1 + if withdrawn_so_far >= MAX_WITHDRAWALS_PER_EPOCH: + break ``` Also perform the following updates: * Set `state.validator_registry_latest_change_slot = state.slot`. * Set `state.shard_committees_at_slots[:EPOCH_LENGTH] = state.shard_committees_at_slots[EPOCH_LENGTH:]`. -* Set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_new_shuffling(state.latest_randao_mixes[(state.slot - EPOCH_LENGTH) % LATEST_RANDAO_MIXES_LENGTH], state.validator_registry, next_start_shard)` where `next_start_shard = (state.shard_committees_at_slots[-1][-1].shard + 1) % SHARD_COUNT`. +* Set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_shuffling(state.latest_randao_mixes[(state.slot - EPOCH_LENGTH) % LATEST_RANDAO_MIXES_LENGTH], state.validator_registry, next_start_shard, state.slot)` where `next_start_shard = (state.shard_committees_at_slots[-1][-1].shard + 1) % SHARD_COUNT`. If a validator registry update does _not_ happen do the following: * Set `state.shard_committees_at_slots[:EPOCH_LENGTH] = state.shard_committees_at_slots[EPOCH_LENGTH:]`. * Let `epochs_since_last_registry_change = (state.slot - state.validator_registry_latest_change_slot) // EPOCH_LENGTH`. * Let `start_shard = state.shard_committees_at_slots[0][0].shard`. -* If `epochs_since_last_registry_change` is an exact power of 2, set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_new_shuffling(state.latest_randao_mixes[(state.slot - EPOCH_LENGTH) % LATEST_RANDAO_MIXES_LENGTH], state.validator_registry, start_shard)`. Note that `start_shard` is not changed from the last epoch. +* If `epochs_since_last_registry_change` is an exact power of 2, set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_shuffling(state.latest_randao_mixes[(state.slot - EPOCH_LENGTH) % LATEST_RANDAO_MIXES_LENGTH], state.validator_registry, start_shard, state.slot)`. Note that `start_shard` is not changed from the last epoch. ### Proposer reshuffling Run the following code to update the shard proposer set: ```python -active_validator_indices = get_active_validator_indices(state.validator_registry) +active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot) num_validators_to_reshuffle = len(active_validator_indices) // SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD for i in range(num_validators_to_reshuffle): # Multiplying i to 2 to ensure we have different input to all the required hashes in the shuffling @@ -1832,6 +1856,7 @@ while len(state.persistent_committee_reassignments) > 0 and state.persistent_com ### Final updates +* Let `e = state.slot // EPOCH_LENGTH`. Set `state.latest_penalized_exit_balances[(e+1) % LATEST_PENALIZED_EXIT_LENGTH] = state.latest_penalized_exit_balances[e % LATEST_PENALIZED_EXIT_LENGTH]` * Remove any `attestation` in `state.latest_attestations` such that `attestation.data.slot < state.slot - EPOCH_LENGTH`. ## State root processing From 85d001001c710a99e3d6b2acd9618b27e255a456 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 28 Dec 2018 12:08:19 -0600 Subject: [PATCH 03/36] Fixed seed lookahead --- 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 fcc521181..1c76d2239 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1818,7 +1818,7 @@ Also perform the following updates: * Set `state.validator_registry_latest_change_slot = state.slot`. * Set `state.shard_committees_at_slots[:EPOCH_LENGTH] = state.shard_committees_at_slots[EPOCH_LENGTH:]`. -* Set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_shuffling(state.latest_randao_mixes[(state.slot - EPOCH_LENGTH) % LATEST_RANDAO_MIXES_LENGTH], state.validator_registry, next_start_shard, state.slot)` where `next_start_shard = (state.shard_committees_at_slots[-1][-1].shard + 1) % SHARD_COUNT`. +* Set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_shuffling(state.latest_randao_mixes[(state.slot - SEED_LOOKAHEAD) % LATEST_RANDAO_MIXES_LENGTH], state.validator_registry, next_start_shard, state.slot)` where `next_start_shard = (state.shard_committees_at_slots[-1][-1].shard + 1) % SHARD_COUNT`. If a validator registry update does _not_ happen do the following: From bd5b32ec5abb7e0d140054b953d8fd25f8961da1 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sat, 29 Dec 2018 13:44:51 -0600 Subject: [PATCH 04/36] Initialize status_flags --- 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 1c76d2239..b075decd8 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1322,6 +1322,7 @@ def process_deposit(state: BeaconState, withdrawal_slot=FAR_FUTURE_SLOT, penalized_slot=FAR_FUTURE_SLOT, exit_count=0, + status_flags=0, poc_commitment=poc_commitment, last_poc_change_slot=0, second_last_poc_change_slot=0, From 14f49aaacb9d8fcb14f32192ab6e7aae4bc2bbff Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sat, 29 Dec 2018 13:47:10 -0600 Subject: [PATCH 05/36] Update specs/core/0_beacon-chain.md Co-Authored-By: vbuterin --- 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 b075decd8..7f5573492 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -168,7 +168,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | `MAX_CASPER_VOTES` | `2**10` (= 1,024) | votes | | `LATEST_BLOCK_ROOTS_LENGTH` | `2**13` (= 8,192) | block roots | | `LATEST_RANDAO_MIXES_LENGTH` | `2**13` (= 8,192) | randao mixes | -| `LATEST_PENALIZED_EXIT_LENGTH` | `2**13` (= 8,192) | cycles | ~36 days | +| `LATEST_PENALIZED_EXIT_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | | `EMPTY_SIGNATURE` | `[bytes48(0), bytes48(0)]` | - | * 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 `EPOCH_LENGTH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.) From 4a9337402584bf24808ead3238b7cd25b2686ad5 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 30 Dec 2018 09:15:23 -0600 Subject: [PATCH 06/36] Fixed as per hww's comments --- specs/core/0_beacon-chain.md | 103 +++++++++++------------------------ 1 file changed, 33 insertions(+), 70 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 7f5573492..b87556a6f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -186,8 +186,8 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | Name | Value | | - | - | -| `INITIAL_FORK_VERSION` | `0` | -| `INITIAL_SLOT_NUMBER` | `0` | +| `GENESIS_FORK_VERSION` | `0` | +| `GENESIS_SLOT` | `0` | | `ZERO_HASH` | `bytes([0] * 32)` | | `FAR_FUTURE_SLOT` | `2**63` | @@ -1013,28 +1013,6 @@ def get_effective_balance(state: State, index: int) -> int: return min(state.validator_balances[index], MAX_DEPOSIT * GWEI_PER_ETH) ``` -#### `get_new_validator_registry_delta_chain_tip` - -```python -def get_new_validator_registry_delta_chain_tip(current_validator_registry_delta_chain_tip: Hash32, - validator_index: int, - pubkey: int, - slot: int, - flag: int) -> Hash32: - """ - Compute the next root in the validator registry delta chain. - """ - return hash_tree_root( - ValidatorRegistryDeltaBlock( - latest_registry_delta_root=current_validator_registry_delta_chain_tip, - validator_index=validator_index, - pubkey=pubkey, - slot=slot, - flag=flag, - ) - ) -``` - #### `get_fork_version` ```python @@ -1144,11 +1122,11 @@ def integer_squareroot(n: int) -> int: ### On startup -A valid block with slot `INITIAL_SLOT_NUMBER` (a "genesis block") has the following values. Other validity rules (e.g. requiring a signature) do not apply. +A valid block with slot `GENESIS_SLOT` (a "genesis block") has the following values. Other validity rules (e.g. requiring a signature) do not apply. ```python { - slot=INITIAL_SLOT_NUMBER, + slot=GENESIS_SLOT, parent_root=ZERO_HASH, state_root=STARTUP_STATE_ROOT, randao_reveal=ZERO_HASH, @@ -1175,18 +1153,18 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], processed_pow_receipt_root: Hash32) -> BeaconState: state = BeaconState( # Misc - slot=INITIAL_SLOT_NUMBER, + slot=GENESIS_SLOT, genesis_time=genesis_time, fork_data=ForkData( - pre_fork_version=INITIAL_FORK_VERSION, - post_fork_version=INITIAL_FORK_VERSION, - fork_slot=INITIAL_SLOT_NUMBER, + pre_fork_version=GENESIS_FORK_VERSION, + post_fork_version=GENESIS_FORK_VERSION, + fork_slot=GENESIS_SLOT, ), # Validator registry validator_registry=[], validator_balances=[], - validator_registry_latest_change_slot=INITIAL_SLOT_NUMBER, + validator_registry_latest_change_slot=GENESIS_SLOT, validator_registry_exit_count=0, validator_registry_delta_chain_tip=ZERO_HASH, @@ -1201,13 +1179,13 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], poc_challenges=[], # Finality - previous_justified_slot=INITIAL_SLOT_NUMBER, - justified_slot=INITIAL_SLOT_NUMBER, + previous_justified_slot=GENESIS_SLOT, + justified_slot=GENESIS_SLOT, justification_bitfield=0, - finalized_slot=INITIAL_SLOT_NUMBER, + finalized_slot=GENESIS_SLOT, # Recent state - latest_crosslinks=[CrosslinkRecord(slot=INITIAL_SLOT_NUMBER, shard_block_root=ZERO_HASH) for _ in range(SHARD_COUNT)], + latest_crosslinks=[CrosslinkRecord(slot=GENESIS_SLOT, shard_block_root=ZERO_HASH) for _ in range(SHARD_COUNT)], latest_block_roots=[ZERO_HASH for _ in range(LATEST_BLOCK_ROOTS_LENGTH)], latest_penalized_exit_balances=[0 for _ in LATEST_PENALIZED_EXIT_LENGTH], latest_attestations=[], @@ -1230,10 +1208,10 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], poc_commitment=deposit.deposit_data.deposit_input.poc_commitment, ) if get_effective_balance(state, validator_index) == MAX_DEPOSIT * GWEI_PER_ETH: - activate_validator(state, validator_index) + activate_validator(state, validator_index, True) # set initial committee shuffling - initial_shuffling = get_shuffling(ZERO_HASH, state.validator_registry, 0, INITIAL_SLOT_NUMBER) + initial_shuffling = get_shuffling(ZERO_HASH, state.validator_registry, 0, GENESIS_SLOT) state.shard_committees_at_slots = initial_shuffling + initial_shuffling # set initial persistent shuffling @@ -1348,45 +1326,34 @@ def process_deposit(state: BeaconState, ### Routines for updating validator status +_Note that all functions in this section mutate `state`_. + ```python -def activate_validator(state: BeaconState, - index: int) -> None: - """ - Activate the validator with the given ``index``. - Note that this function mutates ``state``. - """ +def activate_validator(state: BeaconState, index: int, immediate: bool) -> None: validator = state.validator_registry[index] if validator.activation_slot <= state.slot: return - validator.activation_slot = state.slot + ENTRY_EXIT_DELAY - state.validator_registry_delta_chain_tip = get_new_validator_registry_delta_chain_tip( - current_validator_registry_delta_chain_tip=state.validator_registry_delta_chain_tip, - validator_index=index, - pubkey=validator.pubkey, - slot=validator.activation_slot, - flag=ACTIVATION, + validator.activation_slot = state.slot + (0 if immediate else ENTRY_EXIT_DELAY) + state.validator_registry_delta_chain_tip = hash_tree_root( + ValidatorRegistryDeltaBlock( + current_validator_registry_delta_chain_tip=state.validator_registry_delta_chain_tip, + validator_index=index, + pubkey=validator.pubkey, + slot=validator.activation_slot, + flag=ACTIVATION, + ) ) ``` ```python -def initiate_validator_exit(state: BeaconState, - index: int) -> None: - """ - Initiate exit for the validator with the given ``index``. - Note that this function mutates ``state``. - """ +def initiate_validator_exit(state: BeaconState, index: int) -> None: validator = state.validator_registry[index] validator.status_flags |= INITIATED_EXIT ``` ```python -def exit_validator(state: BeaconState, - index: int) -> None: - """ - Exit the validator with the given ``index``. - Note that this function mutates ``state``. - """ +def exit_validator(state: BeaconState, index: int) -> None: validator = state.validator_registry[index] if validator.exit_slot < state.slot + ENTRY_EXIT_DELAY: @@ -1415,9 +1382,7 @@ def exit_validator(state: BeaconState, ``` ```python -def penalize_validator(state: BeaconState, - index: int) -> None: - +def penalize_validator(state: BeaconState, index: int) -> None: exit_validator(state, index) state.latest_penalized_exit_balances[(state.slot // EPOCH_LENGTH) % LATEST_PENALIZED_EXIT_LENGTH] += get_effective_balance(state, index) @@ -1429,9 +1394,7 @@ def penalize_validator(state: BeaconState, ``` ```python -def set_validator_withdrawable(state: BeaconState, - index: int) -> None: - +def prepare_validator_for_withdrawal(state: BeaconState, index: int) -> None: state.validator_registry[index].status_flags |= WITHDRAWABLE ``` @@ -1758,7 +1721,7 @@ def update_validator_registry(state: BeaconState) -> None: break # Activate validator - activate_validator(state, index) + activate_validator(state, index, False) # Exit validators within the allowable balance churn balance_churn = 0 @@ -1809,7 +1772,7 @@ def process_penalties_and_exits(state: BeaconState) -> None: # TODO: calculate and apply penalties for slashed validators penalty = 1 state.validator_balances[index] -= penalty - set_validator_withdrawable(state, index) + prepare_validator_for_withdrawal(state, index) withdrawn_so_far += 1 if withdrawn_so_far >= MAX_WITHDRAWALS_PER_EPOCH: break From 011970169c5907d8016e0dea8726407f5b8c63c3 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Sun, 30 Dec 2018 20:42:05 -0600 Subject: [PATCH 07/36] Edited as per Justin's comments --- specs/core/0_beacon-chain.md | 37 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index b87556a6f..dbc5c9ded 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -916,7 +916,7 @@ def get_shuffling(seed: Hash32, return output ``` -**Invariant**: if `get_shuffling(seed, validators, shard, slot)` returns some value `x`, it should return the same value `x` for the same `seed` and `shard` and possible future modifications of `validators` forever in phase 0, and until the ~1 year deletion delay in phase 1 and in the future. +**Invariant**: if `get_shuffling(seed, validators, shard, slot)` returns some value `x`, it should return the same value `x` for the same `seed` and `shard` and possible future modifications of `validators` forever in phase 0, and until the ~1 year deletion delay in phase 2 and in the future. Here's a diagram of what is going on: @@ -1207,7 +1207,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], randao_commitment=deposit.deposit_data.deposit_input.randao_commitment, poc_commitment=deposit.deposit_data.deposit_input.poc_commitment, ) - if get_effective_balance(state, validator_index) == MAX_DEPOSIT * GWEI_PER_ETH: + if get_effective_balance(state, validator_index) >= MAX_DEPOSIT * GWEI_PER_ETH: activate_validator(state, validator_index, True) # set initial committee shuffling @@ -1229,9 +1229,8 @@ First, two helper functions: def min_empty_validator_index(validators: List[ValidatorRecord], validator_balances: List[int], current_slot: int) -> int: - for i, v in enumerate(validators): - if v.withdrawal_slot + ZERO_BALANCE_VALIDATOR_TTL <= current_slot: - return i + # In phase 2, add logic that seeks the lowest-index validator that + # has been withdrawn for more than ~1 year return None ``` @@ -1329,12 +1328,10 @@ def process_deposit(state: BeaconState, _Note that all functions in this section mutate `state`_. ```python -def activate_validator(state: BeaconState, index: int, immediate: bool) -> None: +def activate_validator(state: BeaconState, index: int, genesis: bool) -> None: validator = state.validator_registry[index] - if validator.activation_slot <= state.slot: - return - validator.activation_slot = state.slot + (0 if immediate else ENTRY_EXIT_DELAY) + validator.activation_slot = GENESIS_SLOT if genesis else (state.slot + ENTRY_EXIT_DELAY) state.validator_registry_delta_chain_tip = hash_tree_root( ValidatorRegistryDeltaBlock( current_validator_registry_delta_chain_tip=state.validator_registry_delta_chain_tip, @@ -1364,12 +1361,14 @@ def exit_validator(state: BeaconState, index: int) -> None: # The following updates only occur if not previous exited state.validator_registry_exit_count += 1 validator.exit_count = state.validator_registry_exit_count - state.validator_registry_delta_chain_tip = get_new_validator_registry_delta_chain_tip( - current_validator_registry_delta_chain_tip=state.validator_registry_delta_chain_tip, - validator_index=index, - pubkey=validator.pubkey, - slot=validator.exit_slot, - flag=EXIT, + state.validator_registry_delta_chain_tip = hash_tree_root( + ValidatorRegistryDeltaBlock( + current_validator_registry_delta_chain_tip=state.validator_registry_delta_chain_tip, + validator_index=index, + pubkey=validator.pubkey, + slot=validator.exit_slot, + flag=EXIT, + ) ) state.validator_registry_exit_count += 1 @@ -1384,18 +1383,20 @@ def exit_validator(state: BeaconState, index: int) -> None: ```python def penalize_validator(state: BeaconState, index: int) -> None: exit_validator(state, index) + validator = state.validator_registry[index] state.latest_penalized_exit_balances[(state.slot // EPOCH_LENGTH) % LATEST_PENALIZED_EXIT_LENGTH] += get_effective_balance(state, index) whistleblower_index = get_beacon_proposer_index(state, state.slot) whistleblower_reward = get_effective_balance(state, index) // WHISTLEBLOWER_REWARD_QUOTIENT state.validator_balances[whistleblower_index] += whistleblower_reward state.validator_balances[index] -= whistleblower_reward - state.validator_registry[index].penalized_slot = state.slot + validator.penalized_slot = state.slot ``` ```python def prepare_validator_for_withdrawal(state: BeaconState, index: int) -> None: - state.validator_registry[index].status_flags |= WITHDRAWABLE + validator = state.validator_registry[index] + validator.status_flags |= WITHDRAWABLE ``` ## Per-slot processing @@ -1749,7 +1750,7 @@ def process_penalties_and_exits(state: BeaconState) -> None: for i, validator in enumerate(validators): if (state.slot // EPOCH_LENGTH) - (validator.penalized_slot // EPOCH_LENGTH) == LATEST_PENALIZED_EXIT_LENGTH // 2: e = (state.slot // EPOCH_LENGTH) % LATEST_PENALIZED_EXIT_LENGTH - total_at_start = state.latest_penalized_exit_balances[(e+1)%LATEST_PENALIZED_EXIT_LENGTH] + total_at_start = state.latest_penalized_exit_balances[(e + 1) % LATEST_PENALIZED_EXIT_LENGTH] total_at_end = state.latest_penalized_exit_balances[e] total_penalties = total_at_end - total_at_start penalty = get_effective_balance(state, i) * min(total_penalties * 3, total_balance) // total_balance From d36b403c2e9691a43463ad7ec40fb815b4fb6550 Mon Sep 17 00:00:00 2001 From: Justin Date: Mon, 31 Dec 2018 15:14:14 +0000 Subject: [PATCH 08/36] Cleanups and fixes Cleanups * (typo) Remove `get_new_validator_registry_delta_chain_tip` from table of contents * (typo) Update "Routines for updating validator status" in table of contents * Update `FAR_FUTURE_SLOT` from `2**63` to `2**64 - 1` * Put more constants in "Initial values", homogenise * Cleanup note formatting * Remove `ZERO_BALANCE_VALIDATOR_TTL` logic (to be possibly reintroduced in phase 2). * Cleanup `min_empty_validator_index` * Rename `deposit` to `amount` in `process_deposit` and `DepositData`. * (typo) Remove new line under `process_penalties_and_exits` * (typo) "Status codes" => "Status flags" in the table of contents * (typo) `(state.slot - EPOCH_LENGTH) % LATEST_RANDAO_MIXES_LENGTH` => Use `SEED_LOOKAHEAD` instead. * Put `state.validator_registry_latest_change_slot = state.slot` in `update_validator_registry`. * Use `GENESIS_SLOT` for `last_poc_change_slot=0` and `second_last_poc_change_slot=0`. Bugfixes * (typo) `validator_exit` => `exit.validator_index` * Separate initial deposits and initial activations to avoid double activations * Replace `proposer.status != EXITED_WITH_PENALTY` with `validator.penalized_slot > state.slot` in two different places. * Replace `status == EXITED_WITH_PENALTY` with `validator.penalized_slot <= state.slot` (and validator active) in two different places. --- specs/core/0_beacon-chain.md | 86 ++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 49 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index dbc5c9ded..b8a0f62be 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -16,7 +16,7 @@ - [Initial values](#initial-values) - [Time parameters](#time-parameters) - [Reward and penalty quotients](#reward-and-penalty-quotients) - - [Status codes](#status-codes) + - [Status flags](#status-flags) - [Max operations per block](#max-operations-per-block) - [Validator registry delta flags](#validator-registry-delta-flags) - [Signature domains](#signature-domains) @@ -74,7 +74,6 @@ - [`get_attestation_participants`](#get_attestation_participants) - [`bytes1`, `bytes2`, ...](#bytes1-bytes2-) - [`get_effective_balance`](#get_effective_balance) - - [`get_new_validator_registry_delta_chain_tip`](#get_new_validator_registry_delta_chain_tip) - [`get_fork_version`](#get_fork_version) - [`get_domain`](#get_domain) - [`verify_slashable_vote_data`](#verify_slashable_vote_data) @@ -86,7 +85,7 @@ - [`bls_aggregate_pubkeys`](#bls_aggregate_pubkeys) - [On startup](#on-startup) - [Routine for processing deposits](#routine-for-processing-deposits) - - [Routine for updating validator status](#routine-for-updating-validator-status) + - [Routines for updating validator status](#routines-for-updating-validator-status) - [Per-slot processing](#per-slot-processing) - [Misc counters](#misc-counters) - [Block roots](#block-roots) @@ -164,12 +163,10 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | `MAX_BALANCE_CHURN_QUOTIENT` | `2**5` (= 32) | - | | `GWEI_PER_ETH` | `10**9` | Gwei/ETH | | `BEACON_CHAIN_SHARD_NUMBER` | `2**64 - 1` | - | -| `BLS_WITHDRAWAL_PREFIX_BYTE` | `0x00` | - | | `MAX_CASPER_VOTES` | `2**10` (= 1,024) | votes | | `LATEST_BLOCK_ROOTS_LENGTH` | `2**13` (= 8,192) | block roots | | `LATEST_RANDAO_MIXES_LENGTH` | `2**13` (= 8,192) | randao mixes | | `LATEST_PENALIZED_EXIT_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | -| `EMPTY_SIGNATURE` | `[bytes48(0), bytes48(0)]` | - | * 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 `EPOCH_LENGTH * TARGET_COMMITTEE_SIZE`), the shuffling algorithm ensures committee sizes at least `TARGET_COMMITTEE_SIZE`. (Unbiasable randomness with a Verifiable Delay Function (VDF) will improve committee robustness and lower the safe minimum committee size.) @@ -188,8 +185,10 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | - | - | | `GENESIS_FORK_VERSION` | `0` | | `GENESIS_SLOT` | `0` | -| `ZERO_HASH` | `bytes([0] * 32)` | -| `FAR_FUTURE_SLOT` | `2**63` | +| `FAR_FUTURE_SLOT` | `2**64 - 1` | +| `ZERO_HASH` | `bytes32(0)` | +| `EMPTY_SIGNATURE` | `[bytes48(0), bytes48(0)]` | +| `BLS_WITHDRAWAL_PREFIX_BYTE` | `bytes1(0)` | ### Time parameters @@ -202,7 +201,6 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | `ENTRY_EXIT_DELAY` | `2**8` (= 256) | slots | 25.6 minutes | | `POW_RECEIPT_ROOT_VOTING_PERIOD` | `2**10` (= 1,024) | slots | ~1.7 hours | | `SHARD_PERSISTENT_COMMITTEE_CHANGE_PERIOD` | `2**17` (= 131,072) | slots | ~9 days | -| `ZERO_BALANCE_VALIDATOR_TTL` | `2**22` (= 4,194,304) | slots | ~291 days | ### Reward and penalty quotients @@ -373,8 +371,8 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted { # Deposit parameters 'deposit_input': DepositInput, - # Value in Gwei - 'value': 'uint64', + # Amount in Gwei + 'amount': 'uint64', # Timestamp from deposit contract 'timestamp': 'uint64', } @@ -1196,25 +1194,28 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], candidate_pow_receipt_roots=[], ) - # handle initial deposits and activations + # Process initial deposits for deposit in initial_validator_deposits: validator_index = process_deposit( state=state, pubkey=deposit.deposit_data.deposit_input.pubkey, - deposit=deposit.deposit_data.value, + amount=deposit.deposit_data.amount, proof_of_possession=deposit.deposit_data.deposit_input.proof_of_possession, withdrawal_credentials=deposit.deposit_data.deposit_input.withdrawal_credentials, randao_commitment=deposit.deposit_data.deposit_input.randao_commitment, poc_commitment=deposit.deposit_data.deposit_input.poc_commitment, ) + + # Process initial activations + for validator_index, _ in enumerate(state.validator_registry): if get_effective_balance(state, validator_index) >= MAX_DEPOSIT * GWEI_PER_ETH: activate_validator(state, validator_index, True) - # set initial committee shuffling + # Set initial committee shuffling initial_shuffling = get_shuffling(ZERO_HASH, state.validator_registry, 0, GENESIS_SLOT) state.shard_committees_at_slots = initial_shuffling + initial_shuffling - # set initial persistent shuffling + # Set initial persistent shuffling active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot) state.persistent_committees = split(shuffle(active_validator_indices, ZERO_HASH), SHARD_COUNT) @@ -1223,16 +1224,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], ### Routine for processing deposits -First, two helper functions: - -```python -def min_empty_validator_index(validators: List[ValidatorRecord], - validator_balances: List[int], - current_slot: int) -> int: - # In phase 2, add logic that seeks the lowest-index validator that - # has been withdrawn for more than ~1 year - return None -``` +First, a helper function: ```python def validate_proof_of_possession(state: BeaconState, @@ -1266,7 +1258,7 @@ Now, to add a [validator](#dfn-validator) or top up an existing [validator](#dfn ```python def process_deposit(state: BeaconState, pubkey: int, - deposit: int, + amount: int, proof_of_possession: bytes, withdrawal_credentials: Hash32, randao_commitment: Hash32, @@ -1301,31 +1293,27 @@ def process_deposit(state: BeaconState, exit_count=0, status_flags=0, poc_commitment=poc_commitment, - last_poc_change_slot=0, - second_last_poc_change_slot=0, + last_poc_change_slot=GENESIS_SLOT, + second_last_poc_change_slot=GENESIS_SLOT, ) - index = min_empty_validator_index(state.validator_registry, state.validator_balances, state.slot) - if index is None: - state.validator_registry.append(validator) - state.validator_balances.append(deposit) - index = len(state.validator_registry) - 1 - else: - state.validator_registry[index] = validator - state.validator_balances[index] = deposit + # Note: In phase 2 registry indices that has been withdrawn for a long time will be recycled. + index = len(state.validator_registry) + state.validator_registry.append(validator) + state.validator_balances.append(amount) else: - # Increase balance by deposit + # Increase balance by deposit amount index = validator_pubkeys.index(pubkey) assert state.validator_registry[index].withdrawal_credentials == withdrawal_credentials - state.validator_balances[index] += deposit + state.validator_balances[index] += amount return index ``` ### Routines for updating validator status -_Note that all functions in this section mutate `state`_. +Note: All functions in this section mutate `state`. ```python def activate_validator(state: BeaconState, index: int, genesis: bool) -> None: @@ -1455,7 +1443,7 @@ For each `proposer_slashing` in `block.body.proposer_slashings`: * Verify that `proposer_slashing.proposal_data_1.slot == proposer_slashing.proposal_data_2.slot`. * Verify that `proposer_slashing.proposal_data_1.shard == proposer_slashing.proposal_data_2.shard`. * Verify that `proposer_slashing.proposal_data_1.block_root != proposer_slashing.proposal_data_2.block_root`. -* Verify that `proposer.status != EXITED_WITH_PENALTY`. +* Verify that `validator.penalized_slot > state.slot`. * Verify that `bls_verify(pubkey=proposer.pubkey, message=hash_tree_root(proposer_slashing.proposal_data_1), signature=proposer_slashing.proposal_signature_1, domain=get_domain(state.fork_data, proposer_slashing.proposal_data_1.slot, DOMAIN_PROPOSAL))`. * Verify that `bls_verify(pubkey=proposer.pubkey, message=hash_tree_root(proposer_slashing.proposal_data_2), signature=proposer_slashing.proposal_signature_2, domain=get_domain(state.fork_data, proposer_slashing.proposal_data_2.slot, DOMAIN_PROPOSAL))`. * Run `penalize_validator(state, proposer_slashing.proposer_index)`. @@ -1475,7 +1463,7 @@ For each `casper_slashing` in `block.body.casper_slashings`: * Verify that `is_double_vote(slashable_vote_data_1.data, slashable_vote_data_2.data)` or `is_surround_vote(slashable_vote_data_1.data, slashable_vote_data_2.data)`. * Verify that `verify_slashable_vote_data(state, slashable_vote_data_1)`. * Verify that `verify_slashable_vote_data(state, slashable_vote_data_2)`. -* For each [validator](#dfn-validator) index `i` in `intersection`, if `state.validator_registry[i].status` does not equal `EXITED_WITH_PENALTY`, then run `penalize_validator(state, i)`. +* For each [validator](#dfn-validator) index `i` in `intersection` run `penalize_validator(state, i)` if `state.validator_registry[i].penalized_slot > state.slot`. #### Attestations @@ -1503,7 +1491,7 @@ Verify that `len(block.body.deposits) <= MAX_DEPOSITS`. For each `deposit` in `block.body.deposits`: -* Let `serialized_deposit_data` be the serialized form of `deposit.deposit_data`. It should be the `DepositInput` followed by 8 bytes for `deposit_data.value` and 8 bytes for `deposit_data.timestamp`. That is, it should match `deposit_data` in the [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) of which the hash was placed into the Merkle tree. +* Let `serialized_deposit_data` be the serialized form of `deposit.deposit_data`. It should be the `DepositInput` followed by 8 bytes for `deposit_data.amount` and 8 bytes for `deposit_data.timestamp`. That is, it should match `deposit_data` in the [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) of which the hash was placed into the Merkle tree. * Use the following procedure to verify `deposit.merkle_branch`, setting `leaf=serialized_deposit_data`, `depth=DEPOSIT_CONTRACT_TREE_DEPTH` and `root=state.processed_pow_receipt_root`: ```python @@ -1517,14 +1505,13 @@ def verify_merkle_branch(leaf: Hash32, branch: [Hash32], depth: int, index: int, return value == root ``` -* Verify that `state.slot - (deposit.deposit_data.timestamp - state.genesis_time) // SLOT_DURATION < ZERO_BALANCE_VALIDATOR_TTL`. * Run the following: ```python process_deposit( state=state, pubkey=deposit.deposit_data.deposit_input.pubkey, - deposit=deposit.deposit_data.value, + amount=deposit.deposit_data.amount, proof_of_possession=deposit.deposit_data.deposit_input.proof_of_possession, withdrawal_credentials=deposit.deposit_data.deposit_input.withdrawal_credentials, randao_commitment=deposit.deposit_data.deposit_input.randao_commitment, @@ -1542,7 +1529,7 @@ For each `exit` in `block.body.exits`: * Verify that `validator.exit_slot > state.slot + ENTRY_EXIT_DELAY`. * Verify that `state.slot >= exit.slot`. * Verify that `bls_verify(pubkey=validator.pubkey, message=ZERO_HASH, signature=exit.signature, domain=get_domain(state.fork_data, exit.slot, DOMAIN_EXIT))`. -* Run `initiate_validator_exit(state, validator_index)`. +* Run `initiate_validator_exit(state, exit.validator_index)`. #### Miscellaneous @@ -1657,7 +1644,7 @@ Case 2: `epochs_since_finality > 4`: * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_justified_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_boundary_attester_indices`, loses `inactivity_penalty(state, index, epochs_since_finality)`. * Any [active validator](#dfn-active-validator) `index` not in `previous_epoch_head_attester_indices`, loses `base_reward(state, index)`. -* Any [validator](#dfn-validator) `index` with `status == EXITED_WITH_PENALTY`, loses `2 * inactivity_penalty(state, index, epochs_since_finality) + base_reward(state, index)`. +* Any [active_validator](#dfn-active-validator) `index` with `validator.penalized_slot <= state.slot`, loses `2 * inactivity_penalty(state, index, epochs_since_finality) + base_reward(state, index)`. * Any [validator](#dfn-validator) `index` in `previous_epoch_attester_indices` loses `base_reward(state, index) - base_reward(state, index) * MIN_ATTESTATION_INCLUSION_DELAY // inclusion_distance(state, index)` #### Attestation inclusion @@ -1735,13 +1722,15 @@ def update_validator_registry(state: BeaconState) -> None: # Exit validator exit_validator(state, index) + + + state.validator_registry_latest_change_slot = state.slot ``` Regardless of whether the above conditions are satisfied, run the following: ```python def process_penalties_and_exits(state: BeaconState) -> None: - # The active validators active_validator_indices = get_active_validator_indices(state.validator_registry, state.slot) # The total effective balance of active validators @@ -1769,7 +1758,7 @@ def process_penalties_and_exits(state: BeaconState) -> None: withdrawn_so_far = 0 for index in sorted_indices: validator = state.validator_registry[index] - if validator.status == EXITED_WITH_PENALTY: + if validator.penalized_slot <= state.slot: # TODO: calculate and apply penalties for slashed validators penalty = 1 state.validator_balances[index] -= penalty @@ -1781,7 +1770,6 @@ def process_penalties_and_exits(state: BeaconState) -> None: Also perform the following updates: -* Set `state.validator_registry_latest_change_slot = state.slot`. * Set `state.shard_committees_at_slots[:EPOCH_LENGTH] = state.shard_committees_at_slots[EPOCH_LENGTH:]`. * Set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_shuffling(state.latest_randao_mixes[(state.slot - SEED_LOOKAHEAD) % LATEST_RANDAO_MIXES_LENGTH], state.validator_registry, next_start_shard, state.slot)` where `next_start_shard = (state.shard_committees_at_slots[-1][-1].shard + 1) % SHARD_COUNT`. @@ -1790,7 +1778,7 @@ If a validator registry update does _not_ happen do the following: * Set `state.shard_committees_at_slots[:EPOCH_LENGTH] = state.shard_committees_at_slots[EPOCH_LENGTH:]`. * Let `epochs_since_last_registry_change = (state.slot - state.validator_registry_latest_change_slot) // EPOCH_LENGTH`. * Let `start_shard = state.shard_committees_at_slots[0][0].shard`. -* If `epochs_since_last_registry_change` is an exact power of 2, set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_shuffling(state.latest_randao_mixes[(state.slot - EPOCH_LENGTH) % LATEST_RANDAO_MIXES_LENGTH], state.validator_registry, start_shard, state.slot)`. Note that `start_shard` is not changed from the last epoch. +* If `epochs_since_last_registry_change` is an exact power of 2, set `state.shard_committees_at_slots[EPOCH_LENGTH:] = get_shuffling(state.latest_randao_mixes[(state.slot - SEED_LOOKAHEAD) % LATEST_RANDAO_MIXES_LENGTH], state.validator_registry, start_shard, state.slot)`. Note that `start_shard` is not changed from the last epoch. ### Proposer reshuffling From fdb1b6775dd9783830890702001371a0865c8d5d Mon Sep 17 00:00:00 2001 From: vbuterin Date: Mon, 31 Dec 2018 14:08:59 -0600 Subject: [PATCH 09/36] Removed extraneous variable setting --- 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 dce5f0854..71f8a1df3 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1197,7 +1197,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], # Process initial deposits for deposit in initial_validator_deposits: - validator_index = process_deposit( + process_deposit( state=state, pubkey=deposit.deposit_data.deposit_input.pubkey, amount=deposit.deposit_data.amount, From 2105614059a236e2b037539f10a7d4d59ad9e1f4 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Tue, 1 Jan 2019 22:37:43 -0600 Subject: [PATCH 10/36] Bunch of fixes --- specs/core/0_beacon-chain.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 71f8a1df3..a5ed4c786 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -200,6 +200,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | `SEED_LOOKAHEAD` | `2**6` (= 64) | slots | 6.4 minutes | | `ENTRY_EXIT_DELAY` | `2**8` (= 256) | slots | 25.6 minutes | | `POW_RECEIPT_ROOT_VOTING_PERIOD` | `2**10` (= 1,024) | slots | ~1.7 hours | +| `MIN_VALIDATOR_WITHDRAWAL_TIME` | `2**14` (= 16,384) | slots | ~27 hours | ### Reward and penalty quotients @@ -875,6 +876,10 @@ def get_shuffling(seed: Hash32, """ Shuffles ``validators`` into shard committees using ``seed`` as entropy. """ + + # Normalizes slot to start of epoch boundary + slot -= slot % EPOCH_LENGTH + active_validator_indices = get_active_validator_indices(validators, slot) committees_per_slot = max( @@ -1725,8 +1730,8 @@ def process_penalties_and_exits(state: BeaconState) -> None: # The total effective balance of active validators total_balance = sum([get_effective_balance(state, i) for i in active_validator_indices]) - for i, validator in enumerate(validators): - if (state.slot // EPOCH_LENGTH) - (validator.penalized_slot // EPOCH_LENGTH) == LATEST_PENALIZED_EXIT_LENGTH // 2: + for index, validator in enumerate(state.validator_registry): + if (state.slot // EPOCH_LENGTH) == (validator.penalized_slot // EPOCH_LENGTH) + LATEST_PENALIZED_EXIT_LENGTH // 2: e = (state.slot // EPOCH_LENGTH) % LATEST_PENALIZED_EXIT_LENGTH total_at_start = state.latest_penalized_exit_balances[(e + 1) % LATEST_PENALIZED_EXIT_LENGTH] total_at_end = state.latest_penalized_exit_balances[e] @@ -1742,15 +1747,12 @@ def process_penalties_and_exits(state: BeaconState) -> None: else: return state.slot >= validator.exit_slot + MIN_VALIDATOR_WITHDRAWAL_TIME + all_indices = list(range(len(state.validator_registry))) eligible_indices = filter(eligible, all_indices) sorted_indices = sorted(eligible_indices, filter=lambda index: state.validator_registry[index].exit_count) withdrawn_so_far = 0 for index in sorted_indices: validator = state.validator_registry[index] - if validator.penalized_slot <= state.slot: - # TODO: calculate and apply penalties for slashed validators - penalty = 1 - state.validator_balances[index] -= penalty prepare_validator_for_withdrawal(state, index) withdrawn_so_far += 1 if withdrawn_so_far >= MAX_WITHDRAWALS_PER_EPOCH: From 0d9ecce34461a3288696c6dcb401d8d0625a5dd6 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 2 Jan 2019 15:16:55 +0800 Subject: [PATCH 11/36] Rename `poc_` to `custody` --- specs/core/0_beacon-chain.md | 65 ++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index beafa9372..f06e6a6ee 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -113,7 +113,6 @@ - [Crosslinks](#crosslinks-1) - [Ejections](#ejections) - [Validator registry](#validator-registry) - - [Proposer reshuffling](#proposer-reshuffling) - [Final updates](#final-updates) - [State root processing](#state-root-processing) - [References](#references) @@ -289,13 +288,13 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted ```python { # Proof-of-custody indices (0 bits) - 'aggregate_signature_poc_0_indices': '[uint24]', + 'custody_bit_0_indices': ['uint24'], # Proof-of-custody indices (1 bits) - 'aggregate_signature_poc_1_indices': '[uint24]', + 'custody_bit_1_indices': ['uint24'], # Attestation data 'data': AttestationData, # Aggregate signature - 'aggregate_signature': '[uint384]', + 'aggregate_signature': ['uint384'], } ``` @@ -346,7 +345,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted # Attestation data data: AttestationData, # Proof of custody bit - poc_bit: bool, + custody_bit: bool, } ``` @@ -389,7 +388,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted # Initial RANDAO commitment 'randao_commitment': 'hash32', # Initial proof of custody commitment - 'poc_commitment': 'hash32', + 'custody_commitment': 'hash32', # a BLS signature of this ``DepositInput`` 'proof_of_possession': ['uint384'], } @@ -436,15 +435,15 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted 'proposer_slashings': [ProposerSlashing], 'casper_slashings': [CasperSlashing], 'attestations': [Attestation], - 'poc_seed_changes': [ProofOfCustodySeedChange], - 'poc_challenges': [ProofOfCustodyChallenge], - 'poc_responses': [ProofOfCustodyResponse], + 'custody_reseeds': [CustodyReseed], + 'custody_challenges': [CustodyChallenge], + 'custody_responses': [CustodyResponse], 'deposits': [Deposit], 'exits': [Exit], } ``` -`ProofOfCustodySeedChange`, `ProofOfCustodyChallenge`, and `ProofOfCustodyResponse` will be defined in phase 1; for now, put dummy classes as these lists will remain empty throughout phase 0. +`CustodyReseed`, `CustodyChallenge`, and `CustodyResponse` will be defined in phase 1; for now, put dummy classes as these lists will remain empty throughout phase 0. #### `ProposalSignedData` @@ -483,9 +482,9 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted 'shard_committees_at_slots': [[ShardCommittee]], # Proof of custody - # Placeholders for now; ProofOfCustodyChallenge is defined in phase 1, implementers can + # Placeholders for now; CustodyChallenge is defined in phase 1, implementers can # put a dummy class in for now, as the list will remain empty throughout phase 0 - 'poc_challenges': [ProofOfCustodyChallenge], + 'custody_challenges': [CustodyChallenge], # Finality 'previous_justified_slot': 'uint64', @@ -525,10 +524,10 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted # Exit counter when validator exited (or 0) 'exit_count': 'uint64', # Proof of custody commitment - 'poc_commitment': 'hash32', + 'custody_commitment': 'hash32', # Slot the proof of custody seed was last changed - 'last_poc_change_slot': 'uint64', - 'second_last_poc_change_slot': 'uint64', + 'latest_custody_reseed_slot': 'uint64', + 'penultimate_custody_reseed_slot': 'uint64', } ``` @@ -1046,13 +1045,13 @@ def get_domain(fork_data: ForkData, ```python def verify_slashable_vote_data(state: BeaconState, vote_data: SlashableVoteData) -> bool: - if len(vote_data.aggregate_signature_poc_0_indices) + len(vote_data.aggregate_signature_poc_1_indices) > MAX_CASPER_VOTES: + if len(vote_data.custody_bit_0_indices) + len(vote_data.custody_bit_1_indices) > MAX_CASPER_VOTES: return False return bls_verify_multiple( pubkeys=[ - aggregate_pubkey([state.validators[i].pubkey for i in vote_data.aggregate_signature_poc_0_indices]), - aggregate_pubkey([state.validators[i].pubkey for i in vote_data.aggregate_signature_poc_1_indices]), + aggregate_pubkey([state.validators[i].pubkey for i in vote_data.custody_bit_0_indices]), + aggregate_pubkey([state.validators[i].pubkey for i in vote_data.custody_bit_1_indices]), ], messages=[ hash_tree_root(AttestationDataAndCustodyBit(vote_data, False)), @@ -1148,9 +1147,9 @@ A valid block with slot `INITIAL_SLOT_NUMBER` (a "genesis block") has the follow proposer_slashings=[], casper_slashings=[], attestations=[], - poc_seed_changes=[], - poc_challenges=[], - poc_responses=[], + custody_reseeds=[], + custody_challenges=[], + custody_responses=[], deposits=[], exits=[], ), @@ -1186,7 +1185,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], shard_committees_at_slots=[], # Proof of custody - poc_challenges=[], + custody_challenges=[], # Finality previous_justified_slot=INITIAL_SLOT_NUMBER, @@ -1215,7 +1214,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], proof_of_possession=deposit.deposit_data.deposit_input.proof_of_possession, withdrawal_credentials=deposit.deposit_data.deposit_input.withdrawal_credentials, randao_commitment=deposit.deposit_data.deposit_input.randao_commitment, - poc_commitment=deposit.deposit_data.deposit_input.poc_commitment, + custody_commitment=deposit.deposit_data.deposit_input.custody_commitment, ) if get_effective_balance(state, validator_index) == MAX_DEPOSIT * GWEI_PER_ETH: update_validator_status(state, validator_index, ACTIVE) @@ -1247,12 +1246,12 @@ def validate_proof_of_possession(state: BeaconState, proof_of_possession: bytes, withdrawal_credentials: Hash32, randao_commitment: Hash32, - poc_commitment: Hash32) -> bool: + custody_commitment: Hash32) -> bool: proof_of_possession_data = DepositInput( pubkey=pubkey, withdrawal_credentials=withdrawal_credentials, randao_commitment=randao_commitment, - poc_commitment=poc_commitment, + custody_commitment=custody_commitment, proof_of_possession=EMPTY_SIGNATURE, ) @@ -1277,7 +1276,7 @@ def process_deposit(state: BeaconState, proof_of_possession: bytes, withdrawal_credentials: Hash32, randao_commitment: Hash32, - poc_commitment: Hash32) -> int: + custody_commitment: Hash32) -> int: """ Process a deposit from Ethereum 1.0. Note that this function mutates ``state``. @@ -1289,7 +1288,7 @@ def process_deposit(state: BeaconState, proof_of_possession, withdrawal_credentials, randao_commitment, - poc_commitment, + custody_commitment, ) validator_pubkeys = [v.pubkey for v in state.validator_registry] @@ -1304,9 +1303,9 @@ def process_deposit(state: BeaconState, status=PENDING_ACTIVATION, latest_status_change_slot=state.slot, exit_count=0, - poc_commitment=poc_commitment, - last_poc_change_slot=0, - second_last_poc_change_slot=0, + custody_commitment=custody_commitment, + latest_custody_reseed_slot=0, + second_latest_custody_reseed_slot=0, ) index = min_empty_validator_index(state.validator_registry, state.validator_balances, state.slot) @@ -1492,7 +1491,7 @@ For each `casper_slashing` in `block.body.casper_slashings`: * Let `slashable_vote_data_1 = casper_slashing.slashable_vote_data_1`. * Let `slashable_vote_data_2 = casper_slashing.slashable_vote_data_2`. -* Let `indices(slashable_vote_data) = slashable_vote_data.aggregate_signature_poc_0_indices + slashable_vote_data.aggregate_signature_poc_1_indices`. +* Let `indices(slashable_vote_data) = slashable_vote_data.custody_bit_0_indices + slashable_vote_data.custody_bit_1_indices`. * Let `intersection = [x for x in indices(slashable_vote_data_1) if x in indices(slashable_vote_data_2)]`. * Verify that `len(intersection) >= 1`. * Verify that `slashable_vote_data_1.data != slashable_vote_data_2.data`. @@ -1552,7 +1551,7 @@ process_deposit( proof_of_possession=deposit.deposit_data.deposit_input.proof_of_possession, withdrawal_credentials=deposit.deposit_data.deposit_input.withdrawal_credentials, randao_commitment=deposit.deposit_data.deposit_input.randao_commitment, - poc_commitment=deposit.deposit_data.deposit_input.poc_commitment, + custody_commitment=deposit.deposit_data.deposit_input.custody_commitment, ) ``` @@ -1570,7 +1569,7 @@ For each `exit` in `block.body.exits`: #### Miscellaneous -[TO BE REMOVED IN PHASE 1] Verify that `len(block.body.poc_seed_changes) == len(block.body.poc_challenges) == len(block.body.poc_responses) == 0`. +[TO BE REMOVED IN PHASE 1] Verify that `len(block.body.custody_reseeds) == len(block.body.custody_challenges) == len(block.body.custody_responses) == 0`. ## Per-epoch processing From 35adc9c61a6ac1314485029a432f00bd90eb91ca Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 2 Jan 2019 15:31:57 +0800 Subject: [PATCH 12/36] Remove `ShardReassignmentRecord` --- specs/core/0_beacon-chain.md | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index beafa9372..37cd7ccd2 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -46,7 +46,6 @@ - [`ValidatorRecord`](#validatorrecord) - [`CrosslinkRecord`](#crosslinkrecord) - [`ShardCommittee`](#shardcommittee) - - [`ShardReassignmentRecord`](#shardreassignmentrecord) - [`CandidatePoWReceiptRootRecord`](#candidatepowreceiptrootrecord) - [`PendingAttestationRecord`](#pendingattestationrecord) - [`ForkData`](#forkdata) @@ -113,7 +112,6 @@ - [Crosslinks](#crosslinks-1) - [Ejections](#ejections) - [Validator registry](#validator-registry) - - [Proposer reshuffling](#proposer-reshuffling) - [Final updates](#final-updates) - [State root processing](#state-root-processing) - [References](#references) @@ -556,19 +554,6 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted } ``` -#### `ShardReassignmentRecord` - -```python -{ - # Which validator to reassign - 'validator_index': 'uint24', - # To which shard - 'shard': 'uint64', - # When - 'slot': 'uint64', -} -``` - #### `CandidatePoWReceiptRootRecord` ```python From 3dbee05aa6a29392e067fe797940d3a87bbaec2b Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 2 Jan 2019 18:11:31 +0800 Subject: [PATCH 13/36] Rename `pow_receipt_` to `deposit_` --- specs/core/0_beacon-chain.md | 53 ++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index beafa9372..51015e362 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -47,7 +47,7 @@ - [`CrosslinkRecord`](#crosslinkrecord) - [`ShardCommittee`](#shardcommittee) - [`ShardReassignmentRecord`](#shardreassignmentrecord) - - [`CandidatePoWReceiptRootRecord`](#candidatepowreceiptrootrecord) + - [`DepositRootVote`](#depositrootvote) - [`PendingAttestationRecord`](#pendingattestationrecord) - [`ForkData`](#forkdata) - [`ValidatorRegistryDeltaBlock`](#validatorregistrydeltablock) @@ -94,7 +94,7 @@ - [Slot](#slot) - [Proposer signature](#proposer-signature) - [RANDAO](#randao) - - [PoW receipt root](#pow-receipt-root) + - [Deposit root](#deposit-root) - [Operations](#operations) - [Proposer slashings](#proposer-slashings-1) - [Casper slashings](#casper-slashings-1) @@ -104,7 +104,7 @@ - [Miscellaneous](#miscellaneous) - [Per-epoch processing](#per-epoch-processing) - [Helpers](#helpers) - - [Receipt roots](#receipt-roots) + - [Deposit roots](#deposit-roots) - [Justification](#justification) - [Crosslinks](#crosslinks) - [Rewards and penalties](#rewards-and-penalties) @@ -113,7 +113,6 @@ - [Crosslinks](#crosslinks-1) - [Ejections](#ejections) - [Validator registry](#validator-registry) - - [Proposer reshuffling](#proposer-reshuffling) - [Final updates](#final-updates) - [State root processing](#state-root-processing) - [References](#references) @@ -196,7 +195,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | `SLOT_DURATION` | `6` | seconds | 6 seconds | | `MIN_ATTESTATION_INCLUSION_DELAY` | `2**2` (= 4) | slots | 24 seconds | | `EPOCH_LENGTH` | `2**6` (= 64) | slots | 6.4 minutes | -| `POW_RECEIPT_ROOT_VOTING_PERIOD` | `2**10` (= 1,024) | slots | ~1.7 hours | +| `DEPOSIT_ROOT_VOTING_PERIOD` | `2**10` (= 1,024) | slots | ~1.7 hours | | `COLLECTIVE_PENALTY_CALCULATION_PERIOD` | `2**20` (= 1,048,576) | slots | ~73 days | | `ZERO_BALANCE_VALIDATOR_TTL` | `2**22` (= 4,194,304) | slots | ~291 days | @@ -421,7 +420,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted 'parent_root': 'hash32', 'state_root': 'hash32', 'randao_reveal': 'hash32', - 'candidate_pow_receipt_root': 'hash32', + 'deposit_root_vote': 'hash32', 'signature': ['uint384'], ## Body ## @@ -500,9 +499,9 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted 'latest_attestations': [PendingAttestationRecord], 'batched_block_roots': ['hash32'], - # PoW receipt root - 'processed_pow_receipt_root': 'hash32', - 'candidate_pow_receipt_roots': [CandidatePoWReceiptRootRecord], + # Deposit root + 'processed_deposit_root': 'hash32', + 'deposit_root_votes': [DepositRootVote], } ``` @@ -569,12 +568,12 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted } ``` -#### `CandidatePoWReceiptRootRecord` +#### `DepositRootVote` ```python { - # Candidate PoW receipt root - 'candidate_pow_receipt_root': 'hash32', + # Deposit root + 'deposit_root_vote': 'hash32', # Vote count 'vote_count': 'uint64', } @@ -641,7 +640,7 @@ Every deposit, of size between `MIN_DEPOSIT` and `MAX_DEPOSIT`, emits an `Eth1De When sufficiently many full deposits have been made the deposit contract emits the `ChainStart` log. The beacon chain state may then be initialized by calling the `get_initial_beacon_state` function (defined below) where: * `genesis_time` equals `time` in the `ChainStart` log -* `processed_pow_receipt_root` equals `receipt_root` in the `ChainStart` log +* `processed_deposit_root` equals `receipt_root` in the `ChainStart` log * `initial_validator_deposits` is a list of `Deposit` objects built according to the `Eth1Deposit` logs up to the deposit that triggered the `ChainStart` log, processed in the order in which they were emitted (oldest to newest) ### Vyper code @@ -712,7 +711,7 @@ For a beacon chain block, `block`, to be processed by a node, the following cond * The parent block with root `block.parent_root` has been processed and accepted. * The node has processed its `state` up to slot, `block.slot - 1`. -* The Ethereum 1.0 block pointed to by the `state.processed_pow_receipt_root` has been processed and accepted. +* The Ethereum 1.0 block pointed to by the `state.processed_deposit_root` has been processed and accepted. * The node's local clock time is greater than or equal to `state.genesis_time + block.slot * SLOT_DURATION`. If these conditions are not met, the client should delay processing the beacon block until the conditions are all satisfied. @@ -1142,7 +1141,7 @@ A valid block with slot `INITIAL_SLOT_NUMBER` (a "genesis block") has the follow parent_root=ZERO_HASH, state_root=STARTUP_STATE_ROOT, randao_reveal=ZERO_HASH, - candidate_pow_receipt_root=ZERO_HASH, + deposit_root_vote=ZERO_HASH, signature=EMPTY_SIGNATURE, body=BeaconBlockBody( proposer_slashings=[], @@ -1162,7 +1161,7 @@ A valid block with slot `INITIAL_SLOT_NUMBER` (a "genesis block") has the follow ```python def get_initial_beacon_state(initial_validator_deposits: List[Deposit], genesis_time: int, - processed_pow_receipt_root: Hash32) -> BeaconState: + processed_deposit_root: Hash32) -> BeaconState: state = BeaconState( # Misc slot=INITIAL_SLOT_NUMBER, @@ -1201,9 +1200,9 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], latest_attestations=[], batched_block_roots=[], - # PoW receipt root - processed_pow_receipt_root=processed_pow_receipt_root, - candidate_pow_receipt_roots=[], + # Deposit root + processed_deposit_root=processed_deposit_root, + deposit_root_votes=[], ) # handle initial deposits and activations @@ -1462,10 +1461,10 @@ Below are the processing steps that happen at every `block`. * Set `proposer.randao_commitment = block.randao_reveal`. * Set `proposer.randao_layers = 0`. -### PoW receipt root +### Deposit root -* If `block.candidate_pow_receipt_root` is `x.candidate_pow_receipt_root` for some `x` in `state.candidate_pow_receipt_roots`, set `x.vote_count += 1`. -* Otherwise, append to `state.candidate_pow_receipt_roots` a new `CandidatePoWReceiptRootRecord(candidate_pow_receipt_root=block.candidate_pow_receipt_root, vote_count=1)`. +* If `block.deposit_root_vote` is `x.deposit_root_vote` for some `x` in `state.deposit_root_votes`, set `x.vote_count += 1`. +* Otherwise, append to `state.deposit_root_votes` a new `DepositRootVote(deposit_root_vote=block.deposit_root_vote, vote_count=1)`. ### Operations @@ -1528,7 +1527,7 @@ Verify that `len(block.body.deposits) <= MAX_DEPOSITS`. For each `deposit` in `block.body.deposits`: * Let `serialized_deposit_data` be the serialized form of `deposit.deposit_data`. It should be the `DepositInput` followed by 8 bytes for `deposit_data.value` and 8 bytes for `deposit_data.timestamp`. That is, it should match `deposit_data` in the [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) of which the hash was placed into the Merkle tree. -* Use the following procedure to verify `deposit.merkle_branch`, setting `leaf=serialized_deposit_data`, `depth=DEPOSIT_CONTRACT_TREE_DEPTH` and `root=state.processed_pow_receipt_root`: +* Use the following procedure to verify `deposit.merkle_branch`, setting `leaf=serialized_deposit_data`, `depth=DEPOSIT_CONTRACT_TREE_DEPTH` and `root=state.processed_deposit_root`: ```python def verify_merkle_branch(leaf: Hash32, branch: [Hash32], depth: int, index: int, root: Hash32) -> bool: @@ -1621,12 +1620,12 @@ For every `shard_committee` in `state.shard_committees_at_slots`: * Let `inclusion_slot(state, index) = a.slot_included` for the attestation `a` where `index` is in `get_attestation_participants(state, a.data, a.participation_bitfield)`. * Let `inclusion_distance(state, index) = a.slot_included - a.data.slot` where `a` is the above attestation. -### Receipt roots +### Deposit roots -If `state.slot % POW_RECEIPT_ROOT_VOTING_PERIOD == 0`: +If `state.slot % DEPOSIT_ROOT_VOTING_PERIOD == 0`: -* Set `state.processed_pow_receipt_root = x.receipt_root` if `x.vote_count * 2 > POW_RECEIPT_ROOT_VOTING_PERIOD` for some `x` in `state.candidate_pow_receipt_root`. -* Set `state.candidate_pow_receipt_roots = []`. +* Set `state.processed_deposit_root = deposit_root_vote.receipt_root` if `deposit_root_vote.vote_count * 2 > DEPOSIT_ROOT_VOTING_PERIOD` for some `deposit_root_vote` in `state.deposit_root_votes`. +* Set `state.deposit_root_votes = []`. ### Justification From 9a90803f5c3f1cb7a4bcc561c4518ee289ffab5a Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 2 Jan 2019 13:18:35 +0000 Subject: [PATCH 14/36] Update 0_beacon-chain.md * `processed_deposit_root` => `latest_deposit_root` * `receipt_root` => `deposit_root` * `receipt_tree` => `deposit_tree` * Emphasize that deposits are Ethereum 1.0 deposits in text in various places * `Eth1Deposit` => `Deposit` for consistency (Also happy sticking with `Eth1Deposit` and replacing `deposit_` with `eth1_deposit_` everywhere. This may be unnecessary since Ethereum 2.0 deposits can be distinguished with the `shard_` prefix, e.g. `ShardDeposit` and `shard_deposit`.) * Clarify `withdrawal_credentials`. * Clarify that multiple Ethereum 1.0 blocks can have the same deposit root. --- specs/core/0_beacon-chain.md | 59 +++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 51015e362..224510e0e 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -53,7 +53,8 @@ - [`ValidatorRegistryDeltaBlock`](#validatorregistrydeltablock) - [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) - [Deposit arguments](#deposit-arguments) - - [`Eth1Deposit` logs](#eth1deposit-logs) + - [Withdrawal credentials](#withdrawal-credentials) + - [`Deposit` logs](#Deposit-logs) - [`ChainStart` log](#chainstart-log) - [Vyper code](#vyper-code) - [Beacon chain processing](#beacon-chain-processing) @@ -126,7 +127,7 @@ This document represents the specification for Phase 0 of Ethereum 2.0 -- The Beacon Chain. -At the core of Ethereum 2.0 is a system chain called the "beacon chain". The beacon chain stores and manages the registry of [validators](#dfn-validator). In the initial deployment phases of Ethereum 2.0 the only mechanism to become a [validator](#dfn-validator) is to make a one-way ETH transaction to a deposit contract on Ethereum 1.0. Activation as a [validator](#dfn-validator) happens when deposit transaction receipts are processed by the beacon chain, the activation balance is reached, and after a queuing process. Exit is either voluntary or done forcibly as a penalty for misbehavior. +At the core of Ethereum 2.0 is a system chain called the "beacon chain". The beacon chain stores and manages the registry of [validators](#dfn-validator). In the initial deployment phases of Ethereum 2.0 the only mechanism to become a [validator](#dfn-validator) is to make a one-way ETH transaction to a deposit contract on Ethereum 1.0. Activation as a [validator](#dfn-validator) happens when Ethereum 1.0 deposit receipts are processed by the beacon chain, the activation balance is reached, and after a queuing process. Exit is either voluntary or done forcibly as a penalty for misbehavior. The primary source of load on the beacon chain is "attestations". Attestations are availability votes for a shard block, and simultaneously proof of stake votes for a beacon chain block. A sufficient number of attestations for the same shard block create a "crosslink", confirming the shard segment up to that shard block into the beacon chain. Crosslinks also serve as infrastructure for asynchronous cross-shard communication. @@ -368,7 +369,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted ```python { - # Deposit parameters + # Deposit input 'deposit_input': DepositInput, # Value in Gwei 'value': 'uint64', @@ -389,7 +390,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted 'randao_commitment': 'hash32', # Initial proof of custody commitment 'poc_commitment': 'hash32', - # a BLS signature of this ``DepositInput`` + # A BLS signature of this `DepositInput` 'proof_of_possession': ['uint384'], } ``` @@ -499,8 +500,8 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted 'latest_attestations': [PendingAttestationRecord], 'batched_block_roots': ['hash32'], - # Deposit root - 'processed_deposit_root': 'hash32', + # Ethereum 1.0 deposit root + 'latest_deposit_root': 'hash32', 'deposit_root_votes': [DepositRootVote], } ``` @@ -620,28 +621,32 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted ## 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 when the EVM2.0 is deployed and the shards have state. +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. ### Deposit arguments -The deposit contract has a single `deposit` function which takes as argument a SimpleSerialize'd `DepositInput`. One of the `DepositInput` fields is `withdrawal_credentials` which must satisfy: +The deposit contract has a single `deposit` function which takes as argument a SimpleSerialize'd `DepositInput`. + +### Withdrawal credentials + +One of the `DepositInput` fields is `withdrawal_credentials`. It is a commitment to credentials for withdrawals to shards. The first byte of `withdrawal_credentials` is a version number. As of now the only expected format is as follows: * `withdrawal_credentials[:1] == BLS_WITHDRAWAL_PREFIX_BYTE` * `withdrawal_credentials[1:] == hash(withdrawal_pubkey)[1:]` where `withdrawal_pubkey` is a BLS pubkey -We recommend the private key corresponding to `withdrawal_pubkey` be stored in cold storage until a withdrawal is required. +The private key corresponding to `withdrawal_pubkey` will be required to initiate a withdrawal. It can be stored separately until a withdrawal is required, e.g. in cold storage. -### `Eth1Deposit` logs +### `Deposit` logs -Every deposit, of size between `MIN_DEPOSIT` and `MAX_DEPOSIT`, emits an `Eth1Deposit` log for consumption by the beacon chain. The deposit contract does little validation, pushing most of the validator onboarding logic to the beacon chain. In particular, the proof of possession (a BLS12-381 signature) is not verified by the deposit contract. +Every Ethereum 1.0 deposit, of size between `MIN_DEPOSIT` and `MAX_DEPOSIT`, emits an `Deposit` log for consumption by the beacon chain. The deposit contract does little validation, pushing most of the validator onboarding logic to the beacon chain. In particular, the proof of possession (a BLS12-381 signature) is not verified by the deposit contract. ### `ChainStart` log When sufficiently many full deposits have been made the deposit contract emits the `ChainStart` log. The beacon chain state may then be initialized by calling the `get_initial_beacon_state` function (defined below) where: * `genesis_time` equals `time` in the `ChainStart` log -* `processed_deposit_root` equals `receipt_root` in the `ChainStart` log -* `initial_validator_deposits` is a list of `Deposit` objects built according to the `Eth1Deposit` logs up to the deposit that triggered the `ChainStart` log, processed in the order in which they were emitted (oldest to newest) +* `latest_deposit_root` equals `deposit_root` in the `ChainStart` log +* `initial_validator_deposits` is a list of `Deposit` objects built according to the `Deposit` logs up to the deposit that triggered the `ChainStart` log, processed in the order in which they were emitted (oldest to newest) ### Vyper code @@ -656,10 +661,10 @@ DEPOSIT_CONTRACT_TREE_DEPTH: constant(uint256) = 32 TWO_TO_POWER_OF_TREE_DEPTH: constant(uint256) = 4294967296 # 2**32 SECONDS_PER_DAY: constant(uint256) = 86400 -Eth1Deposit: event({previous_receipt_root: bytes32, data: bytes[2064], deposit_count: uint256}) -ChainStart: event({receipt_root: bytes32, time: bytes[8]}) +Deposit: event({previous_deposit_root: bytes32, data: bytes[2064], deposit_count: uint256}) +ChainStart: event({deposit_root: bytes32, time: bytes[8]}) -receipt_tree: map(uint256, bytes32) +deposit_tree: map(uint256, bytes32) deposit_count: uint256 full_deposit_count: uint256 @@ -674,13 +679,13 @@ def deposit(deposit_input: bytes[2048]): timestamp_bytes8: bytes[8] = slice(concat("", convert(block.timestamp, bytes32)), start=24, len=8) deposit_data: bytes[2064] = concat(msg_gwei_bytes8, timestamp_bytes8, deposit_input) - log.Eth1Deposit(self.receipt_tree[1], deposit_data, self.deposit_count) + log.Deposit(self.deposit_tree[1], deposit_data, self.deposit_count) # add deposit to merkle tree - self.receipt_tree[index] = sha3(deposit_data) + self.deposit_tree[index] = sha3(deposit_data) for i in range(32): # DEPOSIT_CONTRACT_TREE_DEPTH (range of constant var not yet supported) index /= 2 - self.receipt_tree[index] = sha3(concat(self.receipt_tree[index * 2], self.receipt_tree[index * 2 + 1])) + self.deposit_tree[index] = sha3(concat(self.deposit_tree[index * 2], self.deposit_tree[index * 2 + 1])) self.deposit_count += 1 if msg.value == as_wei_value(MAX_DEPOSIT, "ether"): @@ -688,12 +693,12 @@ def deposit(deposit_input: bytes[2048]): if self.full_deposit_count == CHAIN_START_FULL_DEPOSIT_THRESHOLD: timestamp_day_boundary: uint256 = as_unitless_number(block.timestamp) - as_unitless_number(block.timestamp) % SECONDS_PER_DAY + SECONDS_PER_DAY timestamp_day_boundary_bytes8: bytes[8] = slice(concat("", convert(timestamp_day_boundary, bytes32)), start=24, len=8) - log.ChainStart(self.receipt_tree[1], timestamp_day_boundary_bytes8) + log.ChainStart(self.deposit_tree[1], timestamp_day_boundary_bytes8) @public @constant -def get_receipt_root() -> bytes32: - return self.receipt_tree[1] +def get_deposit_root() -> bytes32: + return self.deposit_tree[1] ``` @@ -711,7 +716,7 @@ For a beacon chain block, `block`, to be processed by a node, the following cond * The parent block with root `block.parent_root` has been processed and accepted. * The node has processed its `state` up to slot, `block.slot - 1`. -* The Ethereum 1.0 block pointed to by the `state.processed_deposit_root` has been processed and accepted. +* A Ethereum 1.0 block pointed to by the `state.latest_deposit_root` has been processed and accepted. * The node's local clock time is greater than or equal to `state.genesis_time + block.slot * SLOT_DURATION`. If these conditions are not met, the client should delay processing the beacon block until the conditions are all satisfied. @@ -1161,7 +1166,7 @@ A valid block with slot `INITIAL_SLOT_NUMBER` (a "genesis block") has the follow ```python def get_initial_beacon_state(initial_validator_deposits: List[Deposit], genesis_time: int, - processed_deposit_root: Hash32) -> BeaconState: + latest_deposit_root: Hash32) -> BeaconState: state = BeaconState( # Misc slot=INITIAL_SLOT_NUMBER, @@ -1201,7 +1206,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], batched_block_roots=[], # Deposit root - processed_deposit_root=processed_deposit_root, + latest_deposit_root=latest_deposit_root, deposit_root_votes=[], ) @@ -1527,7 +1532,7 @@ Verify that `len(block.body.deposits) <= MAX_DEPOSITS`. For each `deposit` in `block.body.deposits`: * Let `serialized_deposit_data` be the serialized form of `deposit.deposit_data`. It should be the `DepositInput` followed by 8 bytes for `deposit_data.value` and 8 bytes for `deposit_data.timestamp`. That is, it should match `deposit_data` in the [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) of which the hash was placed into the Merkle tree. -* Use the following procedure to verify `deposit.merkle_branch`, setting `leaf=serialized_deposit_data`, `depth=DEPOSIT_CONTRACT_TREE_DEPTH` and `root=state.processed_deposit_root`: +* Use the following procedure to verify `deposit.merkle_branch`, setting `leaf=serialized_deposit_data`, `depth=DEPOSIT_CONTRACT_TREE_DEPTH` and `root=state.latest_deposit_root`: ```python def verify_merkle_branch(leaf: Hash32, branch: [Hash32], depth: int, index: int, root: Hash32) -> bool: @@ -1624,7 +1629,7 @@ For every `shard_committee` in `state.shard_committees_at_slots`: If `state.slot % DEPOSIT_ROOT_VOTING_PERIOD == 0`: -* Set `state.processed_deposit_root = deposit_root_vote.receipt_root` if `deposit_root_vote.vote_count * 2 > DEPOSIT_ROOT_VOTING_PERIOD` for some `deposit_root_vote` in `state.deposit_root_votes`. +* Set `state.latest_deposit_root = deposit_root_vote.deposit_root` if `deposit_root_vote.vote_count * 2 > DEPOSIT_ROOT_VOTING_PERIOD` for some `deposit_root_vote` in `state.deposit_root_votes`. * Set `state.deposit_root_votes = []`. ### Justification From 7d240946a9e3a745515b07cec9b59cf7ff380637 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 2 Jan 2019 13:35:57 +0000 Subject: [PATCH 15/36] Update 0_beacon-chain.md * Cleanup comments related to custody * Rename "Miscellaneous" to "Custody" in the table of contents * Use `INITIAL_SLOT_NUMBER` instead of `0` for initial custody slots * (typo) Fix `second_latest_custody_reseed_slot` => `penultimate_custody_reseed_slot` --- specs/core/0_beacon-chain.md | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index f06e6a6ee..244b1ccea 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -101,7 +101,7 @@ - [Attestations](#attestations-1) - [Deposits](#deposits-1) - [Exits](#exits-1) - - [Miscellaneous](#miscellaneous) + - [Custody](#custody) - [Per-epoch processing](#per-epoch-processing) - [Helpers](#helpers) - [Receipt roots](#receipt-roots) @@ -287,9 +287,9 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted ```python { - # Proof-of-custody indices (0 bits) + # Validator indices with custody bit equal to 0 'custody_bit_0_indices': ['uint24'], - # Proof-of-custody indices (1 bits) + # Validator indices with custody bit equal to 1 'custody_bit_1_indices': ['uint24'], # Attestation data 'data': AttestationData, @@ -308,7 +308,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted 'data': AttestationData, # Attester participation bitfield 'participation_bitfield': 'bytes', - # Proof of custody bitfield + # Custody bitfield 'custody_bitfield': 'bytes', # BLS aggregate signature 'aggregate_signature': ['uint384'], @@ -344,7 +344,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted { # Attestation data data: AttestationData, - # Proof of custody bit + # Custody bit custody_bit: bool, } ``` @@ -387,7 +387,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted 'withdrawal_credentials': 'hash32', # Initial RANDAO commitment 'randao_commitment': 'hash32', - # Initial proof of custody commitment + # Initial custody commitment 'custody_commitment': 'hash32', # a BLS signature of this ``DepositInput`` 'proof_of_possession': ['uint384'], @@ -481,9 +481,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted 'latest_vdf_outputs': ['hash32'], 'shard_committees_at_slots': [[ShardCommittee]], - # Proof of custody - # Placeholders for now; CustodyChallenge is defined in phase 1, implementers can - # put a dummy class in for now, as the list will remain empty throughout phase 0 + # Custody challenges 'custody_challenges': [CustodyChallenge], # Finality @@ -523,10 +521,11 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted 'latest_status_change_slot': 'uint64', # Exit counter when validator exited (or 0) 'exit_count': 'uint64', - # Proof of custody commitment + # Custody commitment 'custody_commitment': 'hash32', - # Slot the proof of custody seed was last changed + # Slot of latest custody reseed 'latest_custody_reseed_slot': 'uint64', + # Slot of second-latest custody reseed 'penultimate_custody_reseed_slot': 'uint64', } ``` @@ -550,7 +549,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted 'shard': 'uint64', # Validator indices 'committee': ['uint24'], - # Total validator count (for proofs of custody) + # Total validator count (for custody challenges) 'total_validator_count': 'uint64', } ``` @@ -587,9 +586,9 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted 'data': AttestationData, # Attester participation bitfield 'participation_bitfield': 'bytes', - # Proof of custody bitfield + # Custody bitfield 'custody_bitfield': 'bytes', - # Slot in which it was included + # Slot the attestation was included 'slot_included': 'uint64', } ``` @@ -1184,7 +1183,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit], latest_vdf_outputs=[ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH // EPOCH_LENGTH)], shard_committees_at_slots=[], - # Proof of custody + # Custody challenges custody_challenges=[], # Finality @@ -1304,8 +1303,8 @@ def process_deposit(state: BeaconState, latest_status_change_slot=state.slot, exit_count=0, custody_commitment=custody_commitment, - latest_custody_reseed_slot=0, - second_latest_custody_reseed_slot=0, + latest_custody_reseed_slot=INITIAL_SLOT_NUMBER, + penultimate_custody_reseed_slot=INITIAL_SLOT_NUMBER, ) index = min_empty_validator_index(state.validator_registry, state.validator_balances, state.slot) @@ -1567,7 +1566,7 @@ For each `exit` in `block.body.exits`: * Verify that `bls_verify(pubkey=validator.pubkey, message=ZERO_HASH, signature=exit.signature, domain=get_domain(state.fork_data, exit.slot, DOMAIN_EXIT))`. * Run `update_validator_status(state, validator_index, new_status=ACTIVE_PENDING_EXIT)`. -#### Miscellaneous +#### Custody [TO BE REMOVED IN PHASE 1] Verify that `len(block.body.custody_reseeds) == len(block.body.custody_challenges) == len(block.body.custody_responses) == 0`. From 696bf23b5e9ea5d357356a0b4341d672e603c83f Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 2 Jan 2019 13:41:32 +0000 Subject: [PATCH 16/36] Remove returned value for process_deposit Also trim trailing whitespace --- specs/core/0_beacon-chain.md | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index a5ed4c786..20471ad80 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -221,7 +221,6 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted | `INITIATED_EXIT` | `2**0` (= 1) | | `WITHDRAWABLE` | `2**1` (= 2) | - ### Max operations per block | Name | Value | @@ -876,17 +875,17 @@ def get_shuffling(seed: Hash32, """ Shuffles ``validators`` into shard committees using ``seed`` as entropy. """ - + # Normalizes slot to start of epoch boundary slot -= slot % EPOCH_LENGTH - + active_validator_indices = get_active_validator_indices(validators, slot) committees_per_slot = max( 1, min( SHARD_COUNT // EPOCH_LENGTH, - len(active_validator_indices) // EPOCH_LENGTH // TARGET_COMMITTEE_SIZE, + len(active_validator_indices) // EPOCH_LENGTH // TARGET_COMMITTEE_SIZE, ) ) @@ -965,7 +964,7 @@ def get_beacon_proposer_index(state: BeaconState, #### `merkle_root` ```python -def merkle_root(values): +def merkle_root(values): """ Merkleize ``values`` (where ``len(values)`` is a power of two) and return the Merkle root. """ @@ -1264,7 +1263,7 @@ def process_deposit(state: BeaconState, proof_of_possession: bytes, withdrawal_credentials: Hash32, randao_commitment: Hash32, - poc_commitment: Hash32) -> int: + poc_commitment: Hash32) -> None: """ Process a deposit from Ethereum 1.0. Note that this function mutates ``state``. @@ -1309,8 +1308,6 @@ def process_deposit(state: BeaconState, assert state.validator_registry[index].withdrawal_credentials == withdrawal_credentials state.validator_balances[index] += amount - - return index ``` ### Routines for updating validator status @@ -1345,7 +1342,7 @@ def exit_validator(state: BeaconState, index: int) -> None: if validator.exit_slot < state.slot + ENTRY_EXIT_DELAY: return - + validator.exit_slot = state.slot + ENTRY_EXIT_DELAY # The following updates only occur if not previous exited @@ -1592,7 +1589,6 @@ If `state.slot % POW_RECEIPT_ROOT_VOTING_PERIOD == 0`: * Set `state.justification_bitfield |= 2` and `state.justified_slot = state.slot - 2 * EPOCH_LENGTH` if `3 * previous_epoch_boundary_attesting_balance >= 2 * total_balance`. * Set `state.justification_bitfield |= 1` and `state.justified_slot = state.slot - 1 * EPOCH_LENGTH` if `3 * current_epoch_boundary_attesting_balance >= 2 * total_balance`. - Set `state.finalized_slot = state.previous_justified_slot` if any of the following are true: * `state.previous_justified_slot == state.slot - 2 * EPOCH_LENGTH and state.justification_bitfield % 4 == 3` @@ -1716,8 +1712,7 @@ def update_validator_registry(state: BeaconState) -> None: # Exit validator exit_validator(state, index) - - + state.validator_registry_latest_change_slot = state.slot ``` @@ -1787,10 +1782,10 @@ This section is divided into Normative and Informative references. Normative re ## Normative ## Informative - _**casper-ffg**_ + _**casper-ffg**_   _Casper the Friendly Finality Gadget_. V. Buterin and V. Griffith. URL: https://arxiv.org/abs/1710.09437 - _**python-poc**_ + _**python-poc**_   _Python proof-of-concept implementation_. Ethereum Foundation. URL: https://github.com/ethereum/beacon_chain # Copyright From 93b18d5160d02d040d5c23dd18c979a1c1b3d0d9 Mon Sep 17 00:00:00 2001 From: Justin Date: Wed, 2 Jan 2019 13:51:57 +0000 Subject: [PATCH 17/36] 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 20471ad80..33fcc5307 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1731,7 +1731,7 @@ def process_penalties_and_exits(state: BeaconState) -> None: total_at_start = state.latest_penalized_exit_balances[(e + 1) % LATEST_PENALIZED_EXIT_LENGTH] total_at_end = state.latest_penalized_exit_balances[e] total_penalties = total_at_end - total_at_start - penalty = get_effective_balance(state, i) * min(total_penalties * 3, total_balance) // total_balance + penalty = get_effective_balance(state, index) * min(total_penalties * 3, total_balance) // total_balance state.validator_balances[index] -= penalty def eligible(index): From f92735454e15dbdda0206b95ae40cfc03dba33b0 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 3 Jan 2019 16:19:54 +0800 Subject: [PATCH 18/36] Fix `verify_slashable_vote_data` The first field of `AttestationDataAndCustodyBit` should be an `AttestationData` object, not an `SlashableVoteData` object. --- 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 7e2bbe83f..a35540d97 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1039,8 +1039,8 @@ def verify_slashable_vote_data(state: BeaconState, vote_data: SlashableVoteData) aggregate_pubkey([state.validators[i].pubkey for i in vote_data.custody_bit_1_indices]), ], messages=[ - hash_tree_root(AttestationDataAndCustodyBit(vote_data, False)), - hash_tree_root(AttestationDataAndCustodyBit(vote_data, True)), + hash_tree_root(AttestationDataAndCustodyBit(vote_data.data, False)), + hash_tree_root(AttestationDataAndCustodyBit(vote_data.data, True)), ], signature=vote_data.aggregate_signature, domain=get_domain( From 457f84fa648997fb06498af2cd6739faa37d816b Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Thu, 3 Jan 2019 12:06:44 -0600 Subject: [PATCH 19/36] Change "justified hash" to "justified slot" There should be a correspondence here but referring to the slot is more explicit, especially for those who are not as familiar with the details of FFG finalization. --- 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 7e2bbe83f..eea62ad13 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1580,7 +1580,7 @@ All [validators](#dfn-validator): * Validators that made an attestation during the previous epoch: * Let `previous_epoch_attestations = [a for a in state.latest_attestations if state.slot - 2 * EPOCH_LENGTH <= a.slot < state.slot - EPOCH_LENGTH]`. * Let `previous_epoch_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in previous_epoch_attestations]`. -* Validators targeting the previous justified hash: +* Validators targeting the previous justified slot: * Let `previous_epoch_justified_attestations = [a for a in current_epoch_attestations + previous_epoch_attestations if a.justified_slot == state.previous_justified_slot]`. * Let `previous_epoch_justified_attester_indices` be the union of the validator index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in previous_epoch_justified_attestations]`. * Let `previous_epoch_justified_attesting_balance = sum([get_effective_balance(state, i) for i in previous_epoch_justified_attester_indices])`. From 9a83ad7b9bdda1058433035d24559136a7a9e466 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Thu, 3 Jan 2019 14:28:36 -0600 Subject: [PATCH 20/36] Fix a type error with units of wei when determining ejection balance. `EJECTION_BALANCE` is in units of ETH. `state.validator_balances[index]` is in units of Gwei. For the ejection computation to work as desired, we need to convert the `EJECTION_BALANCE` constant from ETH to Gwei. --- 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 7e2bbe83f..5c26069ff 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1690,7 +1690,7 @@ def process_ejections(state: BeaconState) -> None: and eject active validators with balance below ``EJECTION_BALANCE``. """ for index in active_validator_indices(state.validator_registry): - if state.validator_balances[index] < EJECTION_BALANCE: + if state.validator_balances[index] < EJECTION_BALANCE * GWEI_PER_ETH: update_validator_status(state, index, new_status=EXITED_WITHOUT_PENALTY) ``` From 19b1a31903330acb167104071e126ef0442649d6 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Thu, 3 Jan 2019 13:57:04 -0800 Subject: [PATCH 21/36] Update 1_shard-data-chains.md --- specs/core/1_shard-data-chains.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/core/1_shard-data-chains.md b/specs/core/1_shard-data-chains.md index a66f37ac5..973a0d332 100644 --- a/specs/core/1_shard-data-chains.md +++ b/specs/core/1_shard-data-chains.md @@ -53,8 +53,8 @@ A `ShardBlock` object has the following fields: # Block signature 'signature': ['uint384'], # Attestation - 'attester_bitfield': 'bytes', - 'aggregate_sig': ['uint384'], + 'participation_bitfield': 'bytes', + 'aggregate_signature': ['uint384'], } ``` @@ -69,9 +69,9 @@ To validate a block header on shard `shard_id`, compute as follows: * Verify that `beacon_chain_ref` is the hash of a block in the beacon chain with slot less than or equal to `slot`. Verify that `beacon_chain_ref` is equal to or a descendant of the `beacon_chain_ref` specified in the `ShardBlock` pointed to by `parent_root`. * Let `state` be the state of the beacon chain block referred to by `beacon_chain_ref`. Let `validators` be `[validators[i] for i in state.current_persistent_committees[shard_id]]`. -* Assert `len(attester_bitfield) == ceil_div8(len(validators))` +* Assert `len(participation_bitfield) == ceil_div8(len(validators))` * Let `proposer_index = hash(state.randao_mix + bytes8(shard_id) + bytes8(slot)) % len(validators)`. Let `msg` be the block but with the `block.signature` set to `[0, 0]`. Verify that `BLSVerify(pub=validators[proposer_index].pubkey, msg=hash(msg), sig=block.signature, domain=get_domain(state, slot, SHARD_PROPOSER_DOMAIN))` passes. -* Generate the `group_public_key` by adding the public keys of all the validators for whom the corresponding position in the bitfield is set to 1. Verify that `BLSVerify(pub=group_public_key, msg=parent_root, sig=block.aggregate_sig, domain=get_domain(state, slot, SHARD_ATTESTER_DOMAIN))` passes. +* Generate the `group_public_key` by adding the public keys of all the validators for whom the corresponding position in the bitfield is set to 1. Verify that `BLSVerify(pub=group_public_key, msg=parent_root, sig=block.aggregate_signature, domain=get_domain(state, slot, SHARD_ATTESTER_DOMAIN))` passes. ### Block Merklization helper From 19a9582a48f97651ecc632c908c2413d5e89625a Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Thu, 3 Jan 2019 19:42:07 -0600 Subject: [PATCH 22/36] Remove dangling `return` statement from spec function This function declares that it returns `None` and instead directly mutates the state. There is a dangling return statement that this commit deletes. --- specs/core/0_beacon-chain.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 7e2bbe83f..b714dea0f 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1759,8 +1759,6 @@ def update_validator_registry(state: BeaconState) -> None: validators_to_penalize = filter(to_penalize, range(len(validator_registry))) for index in validators_to_penalize: state.validator_balances[index] -= get_effective_balance(state, index) * min(total_penalties * 3, total_balance) // total_balance - - return validator_registry, latest_penalized_exit_balances, validator_registry_delta_chain_tip ``` Also perform the following updates: From 3f4be7abb728048cd36b94507ed52507cefaa488 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Thu, 3 Jan 2019 19:42:59 -0600 Subject: [PATCH 23/36] Fixed activation slot conditional. --- 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 2881890c1..1daa1e789 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1676,7 +1676,7 @@ def update_validator_registry(state: BeaconState) -> None: # Activate validators within the allowable balance churn balance_churn = 0 for index, validator in enumerate(state.validator_registry): - if validator.activation_slot > state.slot and state.validator_balances[index] >= MAX_DEPOSIT * GWEI_PER_ETH: + if validator.activation_slot > state.slot + ENTRY_EXIT_DELAY and state.validator_balances[index] >= MAX_DEPOSIT * GWEI_PER_ETH: # Check the balance churn would be within the allowance balance_churn += get_effective_balance(state, index) if balance_churn > max_balance_churn: From a5b1f2cc0ab204eae782632accf6e7a62601f630 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Thu, 3 Jan 2019 19:44:39 -0600 Subject: [PATCH 24/36] Moved comment and changed conditional --- 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 1daa1e789..e86256c29 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1324,12 +1324,12 @@ def initiate_validator_exit(state: BeaconState, index: int) -> None: def exit_validator(state: BeaconState, index: int) -> None: validator = state.validator_registry[index] - if validator.exit_slot < state.slot + ENTRY_EXIT_DELAY: + # The following updates only occur if not previous exited + if validator.exit_slot <= state.slot + ENTRY_EXIT_DELAY: return validator.exit_slot = state.slot + ENTRY_EXIT_DELAY - # The following updates only occur if not previous exited state.validator_registry_exit_count += 1 validator.exit_count = state.validator_registry_exit_count state.validator_registry_delta_chain_tip = hash_tree_root( From c0140d72eee0a191155bef63dd1071aa74f61b92 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Thu, 3 Jan 2019 18:23:23 -0800 Subject: [PATCH 25/36] Update 0_beacon-chain.md --- 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 7e2bbe83f..73789749b 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1597,6 +1597,7 @@ All [validators](#dfn-validator): For every `shard_committee` in `state.shard_committees_at_slots`: +* Let `shard_block_root` be `state.latest_crosslinks[shard_committee.shard].shard_block_root` * Let `attesting_validator_indices(shard_committee, shard_block_root)` be the union of the [validator](#dfn-validator) index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in current_epoch_attestations + previous_epoch_attestations if a.shard == shard_committee.shard and a.shard_block_root == shard_block_root]`. * Let `winning_root(shard_committee)` be equal to the value of `shard_block_root` such that `sum([get_effective_balance(state, i) for i in attesting_validator_indices(shard_committee, shard_block_root)])` is maximized (ties broken by favoring lower `shard_block_root` values). * Let `attesting_validators(shard_committee)` be equal to `attesting_validator_indices(shard_committee, winning_root(shard_committee))` for convenience. @@ -1630,7 +1631,7 @@ Set `state.finalized_slot = state.previous_justified_slot` if any of the followi For every `shard_committee_at_slot` in `state.shard_committees_at_slots` and for every `shard_committee`in `shard_committee_at_slot`: -* Set `state.latest_crosslinks[shard_committee.shard] = CrosslinkRecord(slot=state.slot, block_root=winning_root(shard_committee))` if `3 * total_attesting_balance(shard_committee) >= 2 * total_balance(shard_committee)`. +* Set `state.latest_crosslinks[shard_committee.shard] = CrosslinkRecord(slot=state.slot, shard_block_root=winning_root(shard_committee))` if `3 * total_attesting_balance(shard_committee) >= 2 * total_balance(shard_committee)`. ### Rewards and penalties From 97bd04607c6bb212c4e7419546a571306b647864 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Thu, 3 Jan 2019 19:40:03 -0800 Subject: [PATCH 26/36] 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 73789749b..3001f44bd 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1595,7 +1595,7 @@ All [validators](#dfn-validator): **Note**: `previous_epoch_boundary_attesting_balance` balance might be marginally different than `current_epoch_boundary_attesting_balance` during the previous epoch transition. Due to the tight bound on validator churn each epoch and small per-epoch rewards/penalties, the potential balance difference is very low and only marginally affects consensus safety. -For every `shard_committee` in `state.shard_committees_at_slots`: +For every `shard_committee_at_slot` in `state.shard_committees_at_slots` and for every `shard_committee` in `shard_committee_at_slot`: * Let `shard_block_root` be `state.latest_crosslinks[shard_committee.shard].shard_block_root` * Let `attesting_validator_indices(shard_committee, shard_block_root)` be the union of the [validator](#dfn-validator) index sets given by `[get_attestation_participants(state, a.data, a.participation_bitfield) for a in current_epoch_attestations + previous_epoch_attestations if a.shard == shard_committee.shard and a.shard_block_root == shard_block_root]`. From 36231e698adb091767823ade0dae62d2408300c6 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 4 Jan 2019 13:37:15 +0800 Subject: [PATCH 27/36] Apply suggestions from code review Co-Authored-By: hwwhww --- 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 71a309f87..4ab3882a2 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -623,7 +623,7 @@ The private key corresponding to `withdrawal_pubkey` will be required to initiat ### `Deposit` logs -Every Ethereum 1.0 deposit, of size between `MIN_DEPOSIT` and `MAX_DEPOSIT`, emits an `Deposit` log for consumption by the beacon chain. The deposit contract does little validation, pushing most of the validator onboarding logic to the beacon chain. In particular, the proof of possession (a BLS12-381 signature) is not verified by the deposit contract. +Every Ethereum 1.0 deposit, of size between `MIN_DEPOSIT` and `MAX_DEPOSIT`, emits a `Deposit` log for consumption by the beacon chain. The deposit contract does little validation, pushing most of the validator onboarding logic to the beacon chain. In particular, the proof of possession (a BLS12-381 signature) is not verified by the deposit contract. ### `ChainStart` log @@ -701,7 +701,7 @@ For a beacon chain block, `block`, to be processed by a node, the following cond * The parent block with root `block.parent_root` has been processed and accepted. * The node has processed its `state` up to slot, `block.slot - 1`. -* A Ethereum 1.0 block pointed to by the `state.latest_deposit_root` has been processed and accepted. +* An Ethereum 1.0 block pointed to by the `state.latest_deposit_root` has been processed and accepted. * The node's local clock time is greater than or equal to `state.genesis_time + block.slot * SLOT_DURATION`. If these conditions are not met, the client should delay processing the beacon block until the conditions are all satisfied. From 61b2b6392cb81cb2b86640322d4c132c3526b2fb Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 4 Jan 2019 15:46:06 +0800 Subject: [PATCH 28/36] bugfix and rename Use `deposit_root_vote` for `DepositRootVote` object and `deposit_root` for the deposit root hash. --- specs/core/0_beacon-chain.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/specs/core/0_beacon-chain.md b/specs/core/0_beacon-chain.md index 4ab3882a2..e1c9a3056 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -420,7 +420,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted 'parent_root': 'hash32', 'state_root': 'hash32', 'randao_reveal': 'hash32', - 'deposit_root_vote': 'hash32', + 'deposit_root': 'hash32', 'signature': ['uint384'], ## Body ## @@ -559,7 +559,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted ```python { # Deposit root - 'deposit_root_vote': 'hash32', + 'deposit_root': 'hash32', # Vote count 'vote_count': 'uint64', } @@ -1131,7 +1131,7 @@ A valid block with slot `INITIAL_SLOT_NUMBER` (a "genesis block") has the follow parent_root=ZERO_HASH, state_root=STARTUP_STATE_ROOT, randao_reveal=ZERO_HASH, - deposit_root_vote=ZERO_HASH, + deposit_root=ZERO_HASH, signature=EMPTY_SIGNATURE, body=BeaconBlockBody( proposer_slashings=[], @@ -1453,8 +1453,8 @@ Below are the processing steps that happen at every `block`. ### Deposit root -* If `block.deposit_root_vote` is `x.deposit_root_vote` for some `x` in `state.deposit_root_votes`, set `x.vote_count += 1`. -* Otherwise, append to `state.deposit_root_votes` a new `DepositRootVote(deposit_root_vote=block.deposit_root_vote, vote_count=1)`. +* If `block.deposit_root` is `deposit_root_vote.deposit_root` for some `deposit_root_vote` in `state.deposit_root_votes`, set `deposit_root_vote.vote_count += 1`. +* Otherwise, append to `state.deposit_root_votes` a new `DepositRootVote(deposit_root=block.deposit_root, vote_count=1)`. ### Operations From c24f9070880583dcf3c1fd8fbc62f24b89895d82 Mon Sep 17 00:00:00 2001 From: vbuterin Date: Fri, 4 Jan 2019 09:22:59 -0600 Subject: [PATCH 29/36] Put back spaces --- 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 e86256c29..e8db4268c 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1766,10 +1766,10 @@ This section is divided into Normative and Informative references. Normative re ## Normative ## Informative - _**casper-ffg**_ + _**casper-ffg**_   _Casper the Friendly Finality Gadget_. V. Buterin and V. Griffith. URL: https://arxiv.org/abs/1710.09437 - _**python-poc**_ + _**python-poc**_   _Python proof-of-concept implementation_. Ethereum Foundation. URL: https://github.com/ethereum/beacon_chain # Copyright From a0e454d86ce625aa122ca770d72a5a0eaad035ca Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Fri, 4 Jan 2019 09:48:50 -0600 Subject: [PATCH 30/36] Fixes bug with Merkle proof branch selection This code determines the order in which the next branch element and the current value should be hashed to produce the parent node in the Merkle tree. The existing code fails to verify branches constructed in the standard way. This patch fixes the spec code so that it works properly by using an appropriate parity calculation. Example code here to illustrate it working: https://gist.github.com/ralexstokes/9d82e188bd3286ff74a1fa1dcb5068e0 --- 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 f65ddb050..736a587e1 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1518,7 +1518,7 @@ For each `deposit` in `block.body.deposits`: def verify_merkle_branch(leaf: Hash32, branch: [Hash32], depth: int, index: int, root: Hash32) -> bool: value = leaf for i in range(depth): - if index % 2: + if index // (2**i) % 2: value = hash(branch[i] + value) else: value = hash(value + branch[i]) From fb17be0e9ba1aeae203709593a1add0224ef6db1 Mon Sep 17 00:00:00 2001 From: Cayman Date: Fri, 4 Jan 2019 11:14:27 -0600 Subject: [PATCH 31/36] Fix deserialize uint bounds logic Array accesses should be relative to uint byte length, not bit length. --- specs/simple-serialize.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 63a4d4e31..e3700a2cd 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -248,10 +248,10 @@ size as the integer length. (e.g. ``uint16 == 2 bytes``) All integers are interpreted as **big endian**. ```python -assert(len(rawbytes) >= current_index + int_size) byte_length = int_size / 8 -new_index = current_index + int_size -return int.from_bytes(rawbytes[current_index:current_index+int_size], 'big'), new_index +new_index = current_index + byte_length +assert(len(rawbytes) >= new_index) +return int.from_bytes(rawbytes[current_index:new_index], 'big'), new_index ``` #### Bool From cebbe7b7c5322aae26d4f9eb31cd4951fd645ead Mon Sep 17 00:00:00 2001 From: Cayman Date: Sun, 6 Jan 2019 13:05:27 -0600 Subject: [PATCH 32/36] Fix minor variable name mismatch --- specs/simple-serialize.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 63a4d4e31..ceacb6469 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -384,7 +384,7 @@ values = {} for field_name in get_field_names(typ): field_name_type = get_type_for_field_name(typ, field_name) values[field_name], item_index = deserialize(data, item_index, field_name_type) -assert item_index == start + LENGTH_BYTES + length +assert item_index == new_index return typ(**values), item_index ``` From c69fa9a7939e1b4fb0bd856bf910b306339ed781 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Sun, 6 Jan 2019 17:41:11 -0600 Subject: [PATCH 33/36] Fixes typo in `sorted` function. In keeping with the rest of the code in this document we adhere to valid Python where possible. The custom comparator keyword argument for `sorted` is `key` so this commit updates its usage when sorting validators by exit order. --- 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 e4042c95d..f96dec900 100644 --- a/specs/core/0_beacon-chain.md +++ b/specs/core/0_beacon-chain.md @@ -1729,7 +1729,7 @@ def process_penalties_and_exits(state: BeaconState) -> None: all_indices = list(range(len(state.validator_registry))) eligible_indices = filter(eligible, all_indices) - sorted_indices = sorted(eligible_indices, filter=lambda index: state.validator_registry[index].exit_count) + sorted_indices = sorted(eligible_indices, key=lambda index: state.validator_registry[index].exit_count) withdrawn_so_far = 0 for index in sorted_indices: validator = state.validator_registry[index] From 140ee1ade988f4b8e27d643bb2a44f6c89d1588c Mon Sep 17 00:00:00 2001 From: Cayman Date: Sun, 6 Jan 2019 23:10:33 -0600 Subject: [PATCH 34/36] Fix hash function link --- specs/simple-serialize.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 63a4d4e31..93a3597eb 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -392,7 +392,7 @@ return typ(**values), item_index The below `hash_tree_root` algorithm is defined recursively in the case of lists and containers, and it outputs a value equal to or less than 32 bytes in size. For the final output only (ie. not intermediate outputs), if the output is less than 32 bytes, right-zero-pad it to 32 bytes. The goal is collision resistance *within* each type, not between types. -Refer to [Appendix A](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#appendix-a---hash-function) of Phase 0 of the [Eth2.0 specs](https://github.com/ethereum/eth2.0-specs) for a definition of the hash function used below, `hash(x)`. +Refer to the helper function [hash](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#hash) of Phase 0 of the [Eth2.0 specs](https://github.com/ethereum/eth2.0-specs) for a definition of the hash function used below, `hash(x)`. #### `uint8`..`uint256`, `bool`, `address`, `hash1`..`hash32` From 6fc131433ca2626216a03176b20dd3d64318eebe Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 7 Jan 2019 00:54:40 -0600 Subject: [PATCH 35/36] Update specs/simple-serialize.md Co-Authored-By: wemeetagain --- specs/simple-serialize.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index 93a3597eb..84044318d 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -392,7 +392,7 @@ return typ(**values), item_index The below `hash_tree_root` algorithm is defined recursively in the case of lists and containers, and it outputs a value equal to or less than 32 bytes in size. For the final output only (ie. not intermediate outputs), if the output is less than 32 bytes, right-zero-pad it to 32 bytes. The goal is collision resistance *within* each type, not between types. -Refer to the helper function [hash](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#hash) of Phase 0 of the [Eth2.0 specs](https://github.com/ethereum/eth2.0-specs) for a definition of the hash function used below, `hash(x)`. +Refer to [the helper function `hash`](https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#hash) of Phase 0 of the [Eth2.0 specs](https://github.com/ethereum/eth2.0-specs) for a definition of the hash function used below, `hash(x)`. #### `uint8`..`uint256`, `bool`, `address`, `hash1`..`hash32` From 31d4ca594dce1c7beb3c076b62beb4665f181005 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 7 Jan 2019 08:13:26 -0700 Subject: [PATCH 36/36] conform to standard --- specs/simple-serialize.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/simple-serialize.md b/specs/simple-serialize.md index e3700a2cd..82ac8f809 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -251,7 +251,7 @@ All integers are interpreted as **big endian**. byte_length = int_size / 8 new_index = current_index + byte_length assert(len(rawbytes) >= new_index) -return int.from_bytes(rawbytes[current_index:new_index], 'big'), new_index +return int.from_bytes(rawbytes[current_index:current_index+byte_length], 'big'), new_index ``` #### Bool