diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index 2b4d076a7..e093a7a80 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -512,26 +512,31 @@ Repeat while `slot - last_state_recalculation_slot >= CYCLE_LENGTH`: For every slot `s` in the range `last_state_recalculation_slot - CYCLE_LENGTH ... last_state_recalculation_slot - 1`: -* Determine the total balance of validators that attested to the beacon chain block at slot `s`. -* If the above total balance times three equals or exceeds the total balance of all active validators times two, set `last_justified_slot = max(last_justified_slot, s)` and `justified_streak += 1`. Otherwise, set `justified_streak = 0`. +* Let `total_balance` be the total balance of active validators. +* Let `total_balance_attesting_at_s` be the total balance of validators that attested to the beacon chain block at slot `s`. +* If `3 * total_balance_attesting_at_s >= 2 * total_balance` set `last_justified_slot = max(last_justified_slot, s)` and `justified_streak += 1`. Otherwise set `justified_streak = 0`. * If `justified_streak >= CYCLE_LENGTH + 1` set `last_finalized_slot = max(last_finalized_slot, s - CYCLE_LENGTH - 1)`. -For all `(shard, shard_block_hash)` tuples compute the total balance of validators that attested to the shard block with hash `shard_block_hash`. If this value times three equals or exceeds the total balance of all validators in the committee times two, and the current dynasty exceeds `crosslinks[shard].dynasty`, set `crosslinks[shard] = CrosslinkRecord(dynasty=dynasty, slot=block.last_state_recalculation_slot + CYCLE_LENGTH, hash=shard_block_hash)`. +For every `(shard, shard_block_hash)` tuple: + +* Let `total_balance_attesting_to_h` be the total balance of validators that attested to the shard block with hash `shard_block_hash`. +* Let `total_balance_attestable_to_h` be the total balance in the committee of validators that could have attested to the shard block with hash `shard_block_hash`. +* If `3 * total_balance_attesting_to_h >= 2 * total_balance_attestable_to_h` and `dynasty > crosslinks[shard].dynasty`, set `crosslinks[shard] = CrosslinkRecord(dynasty=dynasty, slot=block.last_state_recalculation_slot + CYCLE_LENGTH, hash=shard_block_hash)`. #### Balance recalculations related to FFG rewards -* Let `total_deposits = sum([v.balance for i, v in enumerate(validators) if i in get_active_validator_indices(validators, dynasty)])`. -* Let `total_deposits_in_eth = total_deposits // 10**9`. -* Let `reward_quotient = BASE_REWARD_QUOTIENT * int_sqrt(total_deposits_in_eth)`. (The per-slot maximum interest rate is `1/reward_quotient`.) +* Let `total_balance` be the total balance of active validators. +* Let `total_balance_in_eth = total_balance // 10**9`. +* Let `reward_quotient = BASE_REWARD_QUOTIENT * int_sqrt(total_balance_in_eth)`. (The per-slot maximum interest rate is `1/reward_quotient`.) * Let `quadratic_penalty_quotient = SQRT_E_DROP_TIME**2`. (The portion lost by offline validators after `D` slots is about `D*D/2/quadratic_penalty_quotient`.) * Let `time_since_finality = block.slot - last_finalized_slot`. For every slot `s` in the range `last_state_recalculation_slot - CYCLE_LENGTH ... last_state_recalculation_slot - 1`: -* Let `total_participated_deposits` be the total balance of validators that voted for the canonical beacon chain block at slot `s`. In the normal case every validator will be in one of the `CYCLE_LENGTH` slots following slot `s` and so can vote for a block at slot `s`. +* Let `total_balance_participating` be the total balance of validators that voted for the canonical beacon chain block at slot `s`. In the normal case every validator will be in one of the `CYCLE_LENGTH` slots following slot `s` and so can vote for a block at slot `s`. * Let `B` be the balance of any given validator whose balance we are adjusting, not including any balance changes from this round of state recalculation. * If `time_since_finality <= 3 * CYCLE_LENGTH` adjust the balance of participating and non-participating validators as follows: - * Participating validators gain `B // reward_quotient * (2 * total_participated_deposits - total_deposits) // total_deposits`. (Note that this value may be negative.) + * Participating validators gain `B // reward_quotient * (2 * total_balance_participating - total_balance) // total_balance`. (Note that this value may be negative.) * Non-participating validators lose `B // reward_quotient`. * Otherwise: * Participating validators gain nothing. @@ -541,16 +546,16 @@ In addition, validators with `status == PENALIZED` lose `B // reward_quotient + #### Balance recalculations related to crosslink rewards -For each shard `S` for which a crosslink committee exists in the cycle prior to the most recent cycle (`last_state_recalculation_slot - CYCLE_LENGTH ... last_state_recalculation_slot - 1`), let `V` be the corresponding validator set. Let `B` be the balance of any given validator whose balance we are adjusting, not including any balance changes from this round of state recalculation. For each `S`, `V`: +For every shard number `shard` for which a crosslink committee exists in the cycle prior to the most recent cycle (`last_state_recalculation_slot - CYCLE_LENGTH ... last_state_recalculation_slot - 1`), let `V` be the corresponding validator set. Let `B` be the balance of any given validator whose balance we are adjusting, not including any balance changes from this round of state recalculation. For each `shard`, `V`: -* Let `total_v_deposits` be the total balance of `V` -* Let `total_participated_v_deposits` be the total balance of the subset of `V` that participated (note that `total_participated_v_deposits <= total_v_deposits`) -* Let `time_since_last_confirmation` be `block.slot - crosslinks[S].slot` -* Adjust balances as follows: - * If `crosslinks[S].dynasty == dynasty`, no reward adjustments - * Otherwise, participating validators' balances are increased by `B // reward_quotient * (2 * total_participated_v_deposits - total_v_deposits) // total_v_deposits`, and the balances of non-participating validators are decreased by `B // reward_quotient + B * time_since_last_confirmation // quadratic_penalty_quotient` +* Let `total_balance_of_v` be the total balance of `V`. +* Let `total_balance_of_v_participating` be the total balance of the subset of `V` that participated. +* Let `time_since_last_confirmation = block.slot - crosslinks[shard].slot`. +* If `dynasty > crosslinks[shard].dynasty` adjust balances as follows: + * Participating validators gain `B // reward_quotient * (2 * total_balance_of_v_participating - total_balance_of_v) // total_balance_of_v`. + * Non-participating validators lose `B // reward_quotient + B * time_since_last_confirmation // quadratic_penalty_quotient`. -Let `committees` be the set of committees processed and `time_since_last_confirmation(c)` be the value of `time_since_last_confirmation` in that committee. Validators with `status == PENALIZED` lose `B // reward_quotient + B * sum([time_since_last_confirmation(c) for c in committees]) // len(committees) // quadratic_penalty_quotient`. +In addition, validators with `status == PENALIZED` lose `B // reward_quotient + B * sum([time_since_last_confirmation(c) for c in committees]) // len(committees) // quadratic_penalty_quotient`, where `committees` is the set of committees processed and `time_since_last_confirmation(c)` is the value of `time_since_last_confirmation` in committee `c`. #### Process penalties, logouts and other special objects @@ -577,7 +582,7 @@ A dynasty transition can happen after a state recalculation if all of the follow * `block.slot - crystallized_state.dynasty_start_slot >= MIN_DYNASTY_LENGTH` * `last_finalized_slot > dynasty_start_slot` -* For every shard `S` in `shard_and_committee_for_slots`, `crosslinks[S].slot > dynasty_start_slot` +* For every shard number `shard` in `shard_and_committee_for_slots`, `crosslinks[shard].slot > dynasty_start_slot` Then, run the following algorithm to update the validator set: @@ -585,12 +590,12 @@ Then, run the following algorithm to update the validator set: def change_validators(validators): # The active validator set active_validators = get_active_validator_indices(validators, dynasty) - # The total size of active deposits - total_deposits = sum([v.balance for i, v in enumerate(validators) if i in active_validators]) + # The total balance of active validators + total_balance = sum([v.balance for i, v in enumerate(validators) if i in active_validators]) # The maximum total wei that can deposit+withdraw max_allowable_change = max( DEPOSIT_SIZE * 2, - total_deposits // MAX_VALIDATOR_CHURN_QUOTIENT + total_balance // MAX_VALIDATOR_CHURN_QUOTIENT ) # Go through the list start to end depositing+withdrawing as many as possible total_changed = 0 @@ -619,7 +624,7 @@ def change_validators(validators): for i in range(len(validators)): if validators[i].status in (PENDING_WITHDRAW, PENALIZED) and current_slot >= validators[i].exit_slot + WITHDRAWAL_PERIOD: if validators[i].status == PENALIZED: - validators[i].balance -= validators[i].balance * min(total_penalties * 3, total_deposits) // total_deposits + validators[i].balance -= validators[i].balance * min(total_penalties * 3, total_balance) // total_balance validators[i].status = WITHDRAWN withdraw_amount = validators[i].balance