From 1cbde6f3c807a092b1e768fb33b1e0065fc69766 Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 5 Oct 2018 11:34:38 +0100 Subject: [PATCH 01/15] Change balances to Gwei See discussion here https://github.com/ethereum/eth2.0-specs/issues/34 --- specs/beacon-chain.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index 16be55464..ca17e206f 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -219,7 +219,7 @@ A `ValidatorRecord` has the following fields: # RANDAO commitment 'randao_commitment': 'hash32', # Balance - 'balance': 'int128', + 'balance': 'int64', # Status code 'status': 'int8', # Slot when validator exited (or 0) @@ -448,7 +448,7 @@ def add_validator(validators, pubkey, proof_of_possession, withdrawal_shard, withdrawal_shard=withdrawal_shard, withdrawal_address=withdrawal_address, randao_commitment=randao_commitment, - balance=DEPOSIT_SIZE, # in WEI + balance=DEPOSIT_SIZE * 10**9, # in Gwei status=PENDING_LOG_IN, exit_slot=0 ) @@ -585,9 +585,9 @@ def change_validators(validators): 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 maximum total wei that can deposit+withdraw + # The maximum total Gwei that can be deposited and withdrawn max_allowable_change = max( - DEPOSIT_SIZE * 2, + 2 * DEPOSIT_SIZE * 10**9, total_deposits // MAX_VALIDATOR_CHURN_QUOTIENT ) # Go through the list start to end depositing+withdrawing as many as possible From e3c1eeb569e0f33779f799c8d41d0a7908c636be Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 5 Oct 2018 11:49:18 +0100 Subject: [PATCH 02/15] Update beacon-chain.md --- specs/beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index ca17e206f..8b427bde5 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -522,8 +522,8 @@ For all (`shard`, `shard_block_hash`) tuples, compute the total deposit size of Let `time_since_finality = block.slot - last_finalized_slot`, and 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. Let: -* `total_deposits = sum([v.balance for i, v in enumerate(validators) if i in get_active_validator_indices(validators, dynasty)])` and `total_deposits_in_ETH = total_deposits // 10**18` -* `reward_quotient = BASE_REWARD_QUOTIENT * int_sqrt(total_deposits_in_ETH)` (`1/reward_quotient` is the per-slot max interest rate) +* `total_deposits = sum([v.balance for i, v in enumerate(validators) if i in get_active_validator_indices(validators, dynasty)])` and `total_deposits_in_gwei = total_deposits // 10**9` +* `reward_quotient = BASE_REWARD_QUOTIENT * int_sqrt(total_deposits_in_gwei)` (`1/reward_quotient` is the per-slot max interest rate) * `quadratic_penalty_quotient = SQRT_E_DROP_TIME**2` (after `D` slots about `D*D/2/quadratic_penalty_quotient` is the portion lost by offline validators) For each slot `S` in the range `last_state_recalculation_slot - CYCLE_LENGTH ... last_state_recalculation_slot - 1`: From ef4b2c45425f481ec0f936c0b81f42814fcf91dd Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 5 Oct 2018 12:01:03 +0100 Subject: [PATCH 03/15] Update beacon-chain.md --- specs/beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index 8b427bde5..c8992f8ae 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -522,8 +522,8 @@ For all (`shard`, `shard_block_hash`) tuples, compute the total deposit size of Let `time_since_finality = block.slot - last_finalized_slot`, and 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. Let: -* `total_deposits = sum([v.balance for i, v in enumerate(validators) if i in get_active_validator_indices(validators, dynasty)])` and `total_deposits_in_gwei = total_deposits // 10**9` -* `reward_quotient = BASE_REWARD_QUOTIENT * int_sqrt(total_deposits_in_gwei)` (`1/reward_quotient` is the per-slot max interest rate) +* `total_deposits = sum([v.balance for i, v in enumerate(validators) if i in get_active_validator_indices(validators, dynasty)])` and `total_deposits_in_ETH = total_deposits // 10**9` +* `reward_quotient = BASE_REWARD_QUOTIENT * int_sqrt(total_deposits_in_ETH)` (`1/reward_quotient` is the per-slot max interest rate) * `quadratic_penalty_quotient = SQRT_E_DROP_TIME**2` (after `D` slots about `D*D/2/quadratic_penalty_quotient` is the portion lost by offline validators) For each slot `S` in the range `last_state_recalculation_slot - CYCLE_LENGTH ... last_state_recalculation_slot - 1`: From 623d3cd31b1752eb6282c083cd3dfb973d579d6d Mon Sep 17 00:00:00 2001 From: Justin Date: Fri, 5 Oct 2018 12:05:08 +0100 Subject: [PATCH 04/15] Cleanups in "State recalculations" --- specs/beacon-chain.md | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index 16be55464..2b4d076a7 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -510,32 +510,34 @@ Repeat while `slot - last_state_recalculation_slot >= CYCLE_LENGTH`: #### Adjust justified slots and crosslink status -For all slots `s` in `last_state_recalculation_slot - CYCLE_LENGTH ... last_state_recalculation_slot - 1`: +For every slot `s` in the range `last_state_recalculation_slot - CYCLE_LENGTH ... last_state_recalculation_slot - 1`: -* Determine the total set of validators that attested to that block at least once -* Determine the total balance of these validators. If this value 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` -* If `justified_streak >= CYCLE_LENGTH + 1`, set `last_finalized_slot = max(last_finalized_slot, s - CYCLE_LENGTH - 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`. +* 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 deposit size of validators that attested to that block hash for that shard. 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 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)`. #### Balance recalculations related to FFG rewards -Let `time_since_finality = block.slot - last_finalized_slot`, and 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. Let: +* 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 `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`. -* `total_deposits = sum([v.balance for i, v in enumerate(validators) if i in get_active_validator_indices(validators, dynasty)])` and `total_deposits_in_ETH = total_deposits // 10**18` -* `reward_quotient = BASE_REWARD_QUOTIENT * int_sqrt(total_deposits_in_ETH)` (`1/reward_quotient` is the per-slot max interest rate) -* `quadratic_penalty_quotient = SQRT_E_DROP_TIME**2` (after `D` slots about `D*D/2/quadratic_penalty_quotient` is the portion lost by offline validators) +For every slot `s` in the range `last_state_recalculation_slot - CYCLE_LENGTH ... last_state_recalculation_slot - 1`: -For each 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 `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.) + * Non-participating validators lose `B // reward_quotient`. +* Otherwise: + * Participating validators gain nothing. + * Non-participating validators lose `B // reward_quotient + B * time_since_finality // quadratic_penalty_quotient`. -* Let `total_participated_deposits` be the total balance of validators that voted for the correct hash in slot `S` (ie. the hash that actually is the hash of the block at that slot in the current chain); note that in the normal case, every validator will be in one of the `CYCLE_LENGTH` slots following the slot and so can vote for a hash in slot `S`. If `time_since_finality <= 3 * CYCLE_LENGTH`, then adjust participating and non-participating validators' balances as follows: - * Participating validators gain `B // reward_quotient * (2 * total_participated_deposits - total_deposits) // total_deposits` (note: this may be negative) - * Nonparticipating validators lose `B // reward_quotient` -* Otherwise, adjust as follows: - * Participating validators' balances are unchanged - * Nonparticipating validators lose `B // reward_quotient + B * time_since_finality // quadratic_penalty_quotient` - -Validators with `status == PENALIZED` also lose `B // reward_quotient + B * time_since_finality // quadratic_penalty_quotient`. +In addition, validators with `status == PENALIZED` lose `B // reward_quotient + B * time_since_finality // quadratic_penalty_quotient`. #### Balance recalculations related to crosslink rewards From fedfe61d6ce571a73af67c280e427305e5261109 Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 6 Oct 2018 13:02:08 +0100 Subject: [PATCH 05/15] Update beacon-chain.md --- specs/beacon-chain.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index c8992f8ae..978e91cbe 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -35,6 +35,7 @@ The primary source of load on the beacon chain are "attestations". Attestations | --- | --- | :---: | - | | `SHARD_COUNT` | 2**10 (= 1,024)| shards | | `DEPOSIT_SIZE` | 2**5 (= 32) | ETH | +| `GWEI_PER_ETH` | 10**9 | Gwei/ETH | | `MIN_COMMITTEE_SIZE` | 2**7 (= 128) | validators | | `GENESIS_TIME` | **TBD** | seconds | | `SLOT_DURATION` | 2**4 (= 16) | seconds | @@ -448,7 +449,7 @@ def add_validator(validators, pubkey, proof_of_possession, withdrawal_shard, withdrawal_shard=withdrawal_shard, withdrawal_address=withdrawal_address, randao_commitment=randao_commitment, - balance=DEPOSIT_SIZE * 10**9, # in Gwei + balance=DEPOSIT_SIZE * GWEI_PER_ETH, # in Gwei status=PENDING_LOG_IN, exit_slot=0 ) @@ -522,7 +523,7 @@ For all (`shard`, `shard_block_hash`) tuples, compute the total deposit size of Let `time_since_finality = block.slot - last_finalized_slot`, and 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. Let: -* `total_deposits = sum([v.balance for i, v in enumerate(validators) if i in get_active_validator_indices(validators, dynasty)])` and `total_deposits_in_ETH = total_deposits // 10**9` +* `total_deposits = sum([v.balance for i, v in enumerate(validators) if i in get_active_validator_indices(validators, dynasty)])` and `total_deposits_in_ETH = total_deposits // GWEI_PER_ETH` * `reward_quotient = BASE_REWARD_QUOTIENT * int_sqrt(total_deposits_in_ETH)` (`1/reward_quotient` is the per-slot max interest rate) * `quadratic_penalty_quotient = SQRT_E_DROP_TIME**2` (after `D` slots about `D*D/2/quadratic_penalty_quotient` is the portion lost by offline validators) @@ -587,7 +588,7 @@ def change_validators(validators): total_deposits = sum([v.balance for i, v in enumerate(validators) if i in active_validators]) # The maximum total Gwei that can be deposited and withdrawn max_allowable_change = max( - 2 * DEPOSIT_SIZE * 10**9, + 2 * DEPOSIT_SIZE * GWEI_PER_ETH, total_deposits // MAX_VALIDATOR_CHURN_QUOTIENT ) # Go through the list start to end depositing+withdrawing as many as possible From 8f65c85455c7ddb17c828e5572986a4971c2f67f Mon Sep 17 00:00:00 2001 From: Justin Date: Sat, 6 Oct 2018 13:32:19 +0100 Subject: [PATCH 06/15] Update beacon-chain.md --- specs/beacon-chain.md | 47 ++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 21 deletions(-) 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 From cbd254a3090771e79ccc8eb8e21c7b32dba93c45 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 8 Oct 2018 20:42:16 -0500 Subject: [PATCH 07/15] replace total_blance_attestable_to_h with total_committee_balance --- specs/beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index e5c54c9c9..12817748c 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -531,8 +531,8 @@ For every slot `s` in the range `last_state_recalculation_slot - CYCLE_LENGTH .. 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)`. +* Let `total_committee_balance` 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_committee_balance` 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 From b62c8c9db14d068b1f3136aaf17c8c5658bac8db Mon Sep 17 00:00:00 2001 From: Yutaro Mori Date: Wed, 10 Oct 2018 16:14:53 +0900 Subject: [PATCH 08/15] Fix lighthouse 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 82b8a5e9a..5e666706d 100644 --- a/specs/simple-serialize.md +++ b/specs/simple-serialize.md @@ -343,6 +343,6 @@ return deserialized_list, new_index | Language | Implementation | Description | |:--------:|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------| | Python | [ https://github.com/ethereum/beacon_chain/blob/master/ssz/ssz.py ](https://github.com/ethereum/beacon_chain/blob/master/ssz/ssz.py) | Beacon chain reference implementation written in Python. | -| Rust | [ https://github.com/sigp/lighthouse/tree/master/ssz ](https://github.com/sigp/lighthouse/tree/master/ssz) | Lighthouse (Rust Ethereum 2.0 Node) maintained SSZ. | +| Rust | [ https://github.com/sigp/lighthouse/tree/master/beacon_chain/utils/ssz ](https://github.com/sigp/lighthouse/tree/master/beacon_chain/utils/ssz) | Lighthouse (Rust Ethereum 2.0 Node) maintained SSZ. | | Nim | [ https://github.com/status-im/nim-beacon-chain/blob/master/beacon_chain/ssz.nim ](https://github.com/status-im/nim-beacon-chain/blob/master/beacon_chain/ssz.nim) | Nim Implementation maintained SSZ. | | Rust | [ https://github.com/paritytech/shasper/tree/master/util/ssz ](https://github.com/paritytech/shasper/tree/master/util/ssz) | Shasper implementation of SSZ maintained by ParityTech. | From 9abb1a0e41cf559bc69cb790c8dd97ab06612732 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Wed, 10 Oct 2018 11:16:13 -0700 Subject: [PATCH 09/15] fixed typo in change_validators --- specs/beacon-chain.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index 12817748c..18afebfa3 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -605,7 +605,7 @@ def change_validators(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( - 2 * DEPOSIT_SIZE GWEI_PER_ETH, + 2 * DEPOSIT_SIZE * GWEI_PER_ETH, total_balance // MAX_VALIDATOR_CHURN_QUOTIENT ) # Go through the list start to end depositing+withdrawing as many as possible @@ -613,7 +613,7 @@ def change_validators(validators): for i in range(len(validators)): if validators[i].status == PENDING_ACTIVATION: validators[i].status = ACTIVE - total_changed += DEPOSIT_SIZE + total_changed += DEPOSIT_SIZE * GWEI_PER_ETH add_validator_set_change_record(crystallized_state, i, validators[i].pubkey, ENTRY) if validators[i].status == PENDING_EXIT: validators[i].status = PENDING_WITHDRAW From 12a1bd24734922dda8bc3d9162d72777f3e7bef0 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 7 Oct 2018 14:21:37 +0800 Subject: [PATCH 10/15] Refactor the sample code and fix #47 --- specs/beacon-chain.md | 161 ++++++++++++++++++++++++++++++------------ 1 file changed, 114 insertions(+), 47 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index 18afebfa3..28a17a897 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -317,61 +317,109 @@ def get_active_validator_indices(validators): Now, a function that shuffles this list: ```python -def shuffle(lst, seed): +def shuffle(values: List[Any], + seed: Hash32) -> List[Any]: + """ + Returns the shuffled ``values`` with seed as entropy. + Mainly for shuffling active validators in-protocol. + """ + values_count = len(values) + # entropy is consumed in 3 byte chunks # rand_max is defined to remove the modulo bias from this entropy source - rand_max = 2**24 - assert len(lst) <= rand_max + SAMPLE_RANGE = 2 ** 24 + assert values_count <= SAMPLE_RANGE: - o = [x for x in lst] + output = [x for x in values] source = seed - i = 0 - while i < len(lst): - source = hash(source) - for pos in range(0, 30, 3): - m = int.from_bytes(source[pos:pos+3], 'big') - remaining = len(lst) - i + index = 0 + while index < values_count: + # Re-hash the source + source = blake(source) + for position in range(0, 30, 3): # gets indices 3 bytes at a time + # Select a 3-byte sampled int + sample_from_source = int.from_bytes(source[position:position + 3], 'big') + # `remaining` is the size of remaining indices of this round + remaining = values_count - index if remaining == 0: break - rand_max = rand_max - rand_max % remaining - if m < rand_max: - replacement_pos = (m % remaining) + i - o[i], o[replacement_pos] = o[replacement_pos], o[i] - i += 1 - return o + + # Set a random maximum bound of sample_from_source + rand_max = SAMPLE_RANGE - SAMPLE_RANGE % remaining + + # Select `replacement_position` with the given `sample_from_source` and `remaining` + if sample_from_source < rand_max: + # Use random number to get `replacement_position`, where it's not `index` + replacement_position = (sample_from_source % remaining) + index + # Swap the index-th and replacement_position-th elements + (output[index], output[replacement_position]) = ( + output[replacement_position], + output[index] + ) + index += 1 + else: + pass + + return output ``` Here's a function that splits a list into `N` pieces: ```python -def split(lst, N): - return [lst[len(lst)*i//N: len(lst)*(i+1)//N] for i in range(N)] +def split(seq: List[Any], pieces: int) -> List[Any]: + """ + Returns the split ``seq`` in ``pieces`` pieces in protocol. + """ + list_length = len(seq) + return [ + seq[(list_length * i // pieces): (list_length * (i + 1) // pieces)] + for i in range(pieces) + ] ``` Now, our combined helper method: ```python -def get_new_shuffling(seed, validators, crosslinking_start_shard): +def get_new_shuffling(seed: Hash32, + validators: List[ValidatorRecord], + crosslinking_start_shard: int) -> List[List[ShardAndCommittee]]: active_validators = get_active_validator_indices(validators) - if len(active_validators) >= CYCLE_LENGTH * MIN_COMMITTEE_SIZE: - committees_per_slot = min(len(active_validators) // CYCLE_LENGTH // (MIN_COMMITTEE_SIZE * 2) + 1, SHARD_COUNT // CYCLE_LENGTH) + active_validators_size = len(active_validators) + + if active_validators_size >= CYCLE_LENGTH * MIN_COMMITTEE_SIZE: + committees_per_slot = min(active_validators_size // CYCLE_LENGTH // (MIN_COMMITTEE_SIZE * 2) + 1, SHARD_COUNT // CYCLE_LENGTH) slots_per_committee = 1 else: committees_per_slot = 1 slots_per_committee = 1 - while len(active_validators) * slots_per_committee < CYCLE_LENGTH * MIN_COMMITTEE_SIZE \ + while active_validators_size * slots_per_committee < CYCLE_LENGTH * MIN_COMMITTEE_SIZE \ and slots_per_committee < CYCLE_LENGTH: slots_per_committee *= 2 - o = [] - for i, slot_indices in enumerate(split(shuffle(active_validators, seed), CYCLE_LENGTH)): + output = [] + + # Shuffle with seed + shuffled_active_validator_indices = shuffle(active_validators, seed) + + # Split the shuffled list into cycle_length pieces + validators_per_slot = split(shuffled_active_validator_indices, CYCLE_LENGTH) + + for slot, slot_indices in enumerate(validators_per_slot): + # Split the shuffled list into committees_per_slot pieces shard_indices = split(slot_indices, committees_per_slot) - shard_start = crosslinking_start_shard + \ - i * committees_per_slot // slots_per_committee - o.append([ShardAndCommittee( - shard = (shard_start + j) % SHARD_COUNT, - committee = indices - ) for j, indices in enumerate(shard_indices)]) - return o + + shard_id_start = crosslinking_start_shard + ( + slot * committees_per_slot // slots_per_committee + ) + shards_and_committees_for_shard_indices = [ + ShardAndCommittee( + shard_id = (shard_id_start + j) % SHARD_COUNT, + committee = indices + ) + for slot, indices in enumerate(shard_indices) + ] + output.append(shards_and_committees_for_shard_indices) + + return output ``` Here's a diagram of what's going on: @@ -381,13 +429,16 @@ Here's a diagram of what's going on: We also define two functions for retrieving data from the state: ```python -def get_shards_and_committees_for_slot(crystallized_state, slot): - earliest_slot_in_array = crystallized_state.last_state_recalculation_slot - CYCLE_LENGTH +def get_shards_and_committees_for_slot(crystallized_state: CrystallizedState, + slot: int) -> List[ShardAndCommittee]: + earliest_slot_in_array = crystallized_state.last_state_recalculation - CYCLE_LENGTH assert earliest_slot_in_array <= slot < earliest_slot_in_array + CYCLE_LENGTH * 2 return crystallized_state.shard_and_committee_for_slots[slot - earliest_slot_in_array] -def get_block_hash(active_state, curblock, slot): - earliest_slot_in_array = curblock.slot - CYCLE_LENGTH * 2 +def get_block_hash(active_state:ActiveState, + current_block: BeaconBlock, + slot: int): + earliest_slot_in_array = current_block.slot - CYCLE_LENGTH * 2 assert earliest_slot_in_array <= slot < earliest_slot_in_array + CYCLE_LENGTH * 2 return active_state.recent_block_hashes[slot - earliest_slot_in_array] ``` @@ -397,7 +448,10 @@ def get_block_hash(active_state, curblock, slot): We define a function to "add a link" to the validator hash chain, used when a validator is added or removed: ```python -def add_validator_set_change_record(crystallized_state, index, pubkey, flag): +def add_validator_set_change_record(crystallized_state: CrystallizedState, + index: int, + pubkey: int, + flag: int) -> None: crystallized_state.validator_set_delta_hash_chain = \ hash(crystallized_state.validator_set_delta_hash_chain + bytes1(flag) + bytes3(index) + bytes32(pubkey)) @@ -406,7 +460,7 @@ def add_validator_set_change_record(crystallized_state, index, pubkey, flag): Finally, we abstractly define `int_sqrt(n)` for use in reward/penalty calculations as the largest integer `k` such that `k**2 <= n`. Here is one possible implementation, though clients are free to use their own including standard libraries for [integer square root](https://en.wikipedia.org/wiki/Integer_square_root) if available and meet the specification. ```python -def int_sqrt(n): +def int_sqrt(n: int): x = n y = (x + 1) // 2 while y < x: @@ -421,13 +475,18 @@ def int_sqrt(n): Run the following code: ```python -def on_startup(initial_validator_entries): +def on_startup(initial_validator_entries: List[Any]) -> None: # Induct validators validators = [] for pubkey, proof_of_possession, withdrawal_shard, withdrawal_address, \ randao_commitment in initial_validator_entries: - add_validator(validators, pubkey, proof_of_possession, - withdrawal_shard, withdrawal_address, randao_commitment) + add_validator(validators, + pubkey, + proof_of_possession, + withdrawal_shard, + withdrawal_address, + randao_commitment + ) # Setup crystallized state cs = CrystallizedState() x = get_new_shuffling(bytes([0] * 32), validators, 0) @@ -447,8 +506,12 @@ The `CrystallizedState()` and `ActiveState()` constructors should initialize all This routine should be run for every validator that is inducted as part of a log created on the PoW chain [TODO: explain where to check for these logs]. These logs should be processed in the order in which they are emitted by the PoW chain. Define `min_empty_validator(validators)` as a function that returns the lowest validator index `i` such that `validators[i].status == WITHDRAWN`, otherwise `None`. ```python -def add_validator(validators, pubkey, proof_of_possession, withdrawal_shard, - withdrawal_address, randao_commitment): +def add_validator(validators: List[ValidatorRecord], + pubkey: int, + proof_of_possession: bytes, + withdrawal_shard: int, + withdrawal_address: Address, + randao_commitment: Hash32) -> int: # if following assert fails, validator induction failed # move on to next validator registration log assert BLSVerify(pub=pubkey, @@ -479,8 +542,10 @@ This procedure should be carried out every block. First, set `recent_block_hashes` to the output of the following, where `parent_hash` is the hash of the immediate previous block (ie. must be equal to `ancestor_hashes[0]`): ```python -def get_new_recent_block_hashes(old_block_hashes, parent_slot, - current_slot, parent_hash): +def get_new_recent_block_hashes(old_block_hashes: List[Hash32], + parent_slot: int, + current_slot: int, + parent_hash: Hash32) -> List[Hash32]: d = current_slot - parent_slot return old_block_hashes[d:] + [parent_hash] * min(d, len(old_block_hashes)) ``` @@ -488,7 +553,9 @@ def get_new_recent_block_hashes(old_block_hashes, parent_slot, The output of `get_block_hash` should not change, except that it will no longer throw for `current_slot - 1`, and will now throw for `current_slot - CYCLE_LENGTH * 2 - 1`. Also, check that the block's `ancestor_hashes` array was correctly updated, using the following algorithm: ```python -def update_ancestor_hashes(parent_ancestor_hashes, parent_slot_number, parent_hash): +def update_ancestor_hashes(parent_ancestor_hashes: List[Hash32], + parent_slot_number: int, + parent_hash: Hash32) -> List[Hash32]: new_ancestor_hashes = copy.copy(parent_ancestor_hashes) for i in range(32): if parent_slot_number % 2**i == 0: @@ -598,9 +665,9 @@ A dynasty transition can happen after a state recalculation if all of the follow Then, run the following algorithm to update the validator set: ```python -def change_validators(validators): +def change_validators(validators: List[ValidatorRecord]) -> None: # The active validator set - active_validators = get_active_validator_indices(validators, dynasty) + active_validators = get_active_validator_indices(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 From f1da3c55cd1ba1d94e0feb365571a5e17e356b73 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 7 Oct 2018 14:39:17 +0800 Subject: [PATCH 11/15] minor fix --- specs/beacon-chain.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index 28a17a897..5effc61a9 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -321,7 +321,6 @@ def shuffle(values: List[Any], seed: Hash32) -> List[Any]: """ Returns the shuffled ``values`` with seed as entropy. - Mainly for shuffling active validators in-protocol. """ values_count = len(values) @@ -437,7 +436,7 @@ def get_shards_and_committees_for_slot(crystallized_state: CrystallizedState, def get_block_hash(active_state:ActiveState, current_block: BeaconBlock, - slot: int): + slot: int) -> Hash32: earliest_slot_in_array = current_block.slot - CYCLE_LENGTH * 2 assert earliest_slot_in_array <= slot < earliest_slot_in_array + CYCLE_LENGTH * 2 return active_state.recent_block_hashes[slot - earliest_slot_in_array] From 0cc1b6c4a40c58f382d2bc9e527ba4c76a766156 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 7 Oct 2018 14:41:09 +0800 Subject: [PATCH 12/15] fix shuffle --- specs/beacon-chain.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index 5effc61a9..69c5b521e 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -326,8 +326,8 @@ def shuffle(values: List[Any], # entropy is consumed in 3 byte chunks # rand_max is defined to remove the modulo bias from this entropy source - SAMPLE_RANGE = 2 ** 24 - assert values_count <= SAMPLE_RANGE: + rand_max = 2 ** 24 + assert values_count <= rand_max: output = [x for x in values] source = seed @@ -344,7 +344,7 @@ def shuffle(values: List[Any], break # Set a random maximum bound of sample_from_source - rand_max = SAMPLE_RANGE - SAMPLE_RANGE % remaining + rand_max = rand_max - rand_max % remaining # Select `replacement_position` with the given `sample_from_source` and `remaining` if sample_from_source < rand_max: From 80a61c1c6228d53f4b1b9a0d60b1b6735253e069 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Sun, 7 Oct 2018 22:48:37 +0800 Subject: [PATCH 13/15] PR feedback and some minor fix --- specs/beacon-chain.md | 60 ++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index 69c5b521e..8ddf99419 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -334,13 +334,13 @@ def shuffle(values: List[Any], index = 0 while index < values_count: # Re-hash the source - source = blake(source) + source = hash(source) for position in range(0, 30, 3): # gets indices 3 bytes at a time # Select a 3-byte sampled int sample_from_source = int.from_bytes(source[position:position + 3], 'big') # `remaining` is the size of remaining indices of this round remaining = values_count - index - if remaining == 0: + if remaining == 1: break # Set a random maximum bound of sample_from_source @@ -351,10 +351,7 @@ def shuffle(values: List[Any], # Use random number to get `replacement_position`, where it's not `index` replacement_position = (sample_from_source % remaining) + index # Swap the index-th and replacement_position-th elements - (output[index], output[replacement_position]) = ( - output[replacement_position], - output[index] - ) + output[index], output[replacement_position] = output[replacement_position], output[index] index += 1 else: pass @@ -362,7 +359,7 @@ def shuffle(values: List[Any], return output ``` -Here's a function that splits a list into `N` pieces: +Here's a function that splits a list into `pieces` pieces: ```python def split(seq: List[Any], pieces: int) -> List[Any]: @@ -394,6 +391,7 @@ def get_new_shuffling(seed: Hash32, while active_validators_size * slots_per_committee < CYCLE_LENGTH * MIN_COMMITTEE_SIZE \ and slots_per_committee < CYCLE_LENGTH: slots_per_committee *= 2 + output = [] # Shuffle with seed @@ -411,8 +409,8 @@ def get_new_shuffling(seed: Hash32, ) shards_and_committees_for_shard_indices = [ ShardAndCommittee( - shard_id = (shard_id_start + j) % SHARD_COUNT, - committee = indices + shard_id=(shard_id_start + j) % SHARD_COUNT, + committee=indices ) for slot, indices in enumerate(shard_indices) ] @@ -459,7 +457,7 @@ def add_validator_set_change_record(crystallized_state: CrystallizedState, Finally, we abstractly define `int_sqrt(n)` for use in reward/penalty calculations as the largest integer `k` such that `k**2 <= n`. Here is one possible implementation, though clients are free to use their own including standard libraries for [integer square root](https://en.wikipedia.org/wiki/Integer_square_root) if available and meet the specification. ```python -def int_sqrt(n: int): +def int_sqrt(n: int) -> int: x = n y = (x + 1) // 2 while y < x: @@ -479,23 +477,33 @@ def on_startup(initial_validator_entries: List[Any]) -> None: validators = [] for pubkey, proof_of_possession, withdrawal_shard, withdrawal_address, \ randao_commitment in initial_validator_entries: - add_validator(validators, - pubkey, - proof_of_possession, - withdrawal_shard, - withdrawal_address, - randao_commitment + add_validator( + validators=validators, + pubkey=pubkey, + proof_of_possession=proof_of_possession, + withdrawal_shard=withdrawal_shard, + withdrawal_address=withdrawal_address, + randao_commitment=randao_commitment ) # Setup crystallized state cs = CrystallizedState() x = get_new_shuffling(bytes([0] * 32), validators, 0) cs.shard_and_committee_for_slots = x + x cs.dynasty = 1 - cs.crosslinks = [CrosslinkRecord(dynasty=0, slot=0, hash=bytes([0] * 32)) - for i in range(SHARD_COUNT)] + cs.crosslinks = [ + CrosslinkRecord( + dynasty=0, + slot=0, + hash=bytes([0] * 32) + ) + for i in range(SHARD_COUNT) + ] # Setup active state as = ActiveState() - as.recent_block_hashes = [bytes([0] * 32) for _ in range(CYCLE_LENGTH * 2)] + as.recent_block_hashes = [ + bytes([0] * 32) + for _ in range(CYCLE_LENGTH * 2) + ] ``` The `CrystallizedState()` and `ActiveState()` constructors should initialize all values to zero bytes, an empty value or an empty array depending on context. The `add_validator` routine is defined below. @@ -680,12 +688,22 @@ def change_validators(validators: List[ValidatorRecord]) -> None: if validators[i].status == PENDING_ACTIVATION: validators[i].status = ACTIVE total_changed += DEPOSIT_SIZE * GWEI_PER_ETH - add_validator_set_change_record(crystallized_state, i, validators[i].pubkey, ENTRY) + add_validator_set_change_record( + crystallized_state=crystallized_state, + index=i, + pubkey=validators[i].pubkey, + flag=ENTRY + ) if validators[i].status == PENDING_EXIT: validators[i].status = PENDING_WITHDRAW validators[i].exit_slot = current_slot total_changed += validators[i].balance - add_validator_set_change_record(crystallized_state, i, validators[i].pubkey, EXIT) + add_validator_set_change_record( + crystallized_state=crystallized_state, + index=i, + pubkey=validators[i].pubkey, + flag=EXIT + ) if total_changed >= max_allowable_change: break From 39a5bac337a77e6d2ebaee41217eb9e48ff5634c Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 9 Oct 2018 15:33:22 +0800 Subject: [PATCH 14/15] PR feedback and minor fix --- specs/beacon-chain.md | 55 ++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index 8ddf99419..e75925219 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -359,17 +359,17 @@ def shuffle(values: List[Any], return output ``` -Here's a function that splits a list into `pieces` pieces: +Here's a function that splits a list into `split_count` pieces: ```python -def split(seq: List[Any], pieces: int) -> List[Any]: +def split(seq: List[Any], split_count: int) -> List[Any]: """ - Returns the split ``seq`` in ``pieces`` pieces in protocol. + Returns the split ``seq`` in ``split_count`` pieces in protocol. """ list_length = len(seq) return [ - seq[(list_length * i // pieces): (list_length * (i + 1) // pieces)] - for i in range(pieces) + seq[(list_length * i // split_count): (list_length * (i + 1) // split_count)] + for i in range(split_count) ] ``` @@ -404,8 +404,9 @@ def get_new_shuffling(seed: Hash32, # Split the shuffled list into committees_per_slot pieces shard_indices = split(slot_indices, committees_per_slot) - shard_id_start = crosslinking_start_shard + ( - slot * committees_per_slot // slots_per_committee + shard_id_start = ( + crosslinking_start_shard + + (slot * committees_per_slot // slots_per_committee) ) shards_and_committees_for_shard_indices = [ ShardAndCommittee( @@ -432,7 +433,7 @@ def get_shards_and_committees_for_slot(crystallized_state: CrystallizedState, assert earliest_slot_in_array <= slot < earliest_slot_in_array + CYCLE_LENGTH * 2 return crystallized_state.shard_and_committee_for_slots[slot - earliest_slot_in_array] -def get_block_hash(active_state:ActiveState, +def get_block_hash(active_state: ActiveState, current_block: BeaconBlock, slot: int) -> Hash32: earliest_slot_in_array = current_block.slot - CYCLE_LENGTH * 2 @@ -472,7 +473,7 @@ def int_sqrt(n: int) -> int: Run the following code: ```python -def on_startup(initial_validator_entries: List[Any]) -> None: +def on_startup(initial_validator_entries: List[Any]) -> Tuple[CrystallizedState, ActiveState]: # Induct validators validators = [] for pubkey, proof_of_possession, withdrawal_shard, withdrawal_address, \ @@ -486,11 +487,8 @@ def on_startup(initial_validator_entries: List[Any]) -> None: randao_commitment=randao_commitment ) # Setup crystallized state - cs = CrystallizedState() x = get_new_shuffling(bytes([0] * 32), validators, 0) - cs.shard_and_committee_for_slots = x + x - cs.dynasty = 1 - cs.crosslinks = [ + crosslinks = [ CrosslinkRecord( dynasty=0, slot=0, @@ -498,12 +496,37 @@ def on_startup(initial_validator_entries: List[Any]) -> None: ) for i in range(SHARD_COUNT) ] + crystallized_state = CrystallizedState( + dynasty=1, + dynasty_seed=bytes([0] * 32), # stub + dynasty_start_slot=0, + validators=validators, + crosslinks=crosslinks, + last_state_recalculation_slot=0, + last_finalized_slot=0, + last_justified_slot=0, + justified_streak=0, + shard_and_committee_for_slots=x + x, + deposits_penalized_in_period=[], + validator_set_delta_hash_chain=bytes([0] * 32), # stub + pre_fork_version=0, + post_fork_version=0, + fork_slot_number=0 + ) + # Setup active state - as = ActiveState() - as.recent_block_hashes = [ + recent_block_hashes = [ bytes([0] * 32) for _ in range(CYCLE_LENGTH * 2) ] + active_state = ActiveState( + pending_attestations=[], + pending_specials=[], + recent_block_hashes=recent_block_hashes, + randao_mix=bytes([0] * 32) # stub + ) + + return crystallized_state, active_state ``` The `CrystallizedState()` and `ActiveState()` constructors should initialize all values to zero bytes, an empty value or an empty array depending on context. The `add_validator` routine is defined below. @@ -611,7 +634,7 @@ For every `(shard, shard_block_hash)` tuple: #### Balance recalculations related to FFG rewards * Let `total_balance` be the total balance of active validators. -* Let `total_balance_in_eth = total_balance // GWEI_PER_ETH. +* Let `total_balance_in_eth = total_balance // GWEI_PER_ETH`. * 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`. From 31eb7e9a052c9e9b79c5a7300bfbf78a41054c0c Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Thu, 11 Oct 2018 02:47:40 +0800 Subject: [PATCH 15/15] Rename rand_max to sample_max --- specs/beacon-chain.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/specs/beacon-chain.md b/specs/beacon-chain.md index e75925219..ab397fb06 100644 --- a/specs/beacon-chain.md +++ b/specs/beacon-chain.md @@ -325,9 +325,9 @@ def shuffle(values: List[Any], values_count = len(values) # entropy is consumed in 3 byte chunks - # rand_max is defined to remove the modulo bias from this entropy source - rand_max = 2 ** 24 - assert values_count <= rand_max: + # sample_max is defined to remove the modulo bias from this entropy source + sample_max = 2 ** 24 + assert values_count <= sample_max output = [x for x in values] source = seed @@ -344,10 +344,10 @@ def shuffle(values: List[Any], break # Set a random maximum bound of sample_from_source - rand_max = rand_max - rand_max % remaining + sample_max = sample_max - sample_max % remaining # Select `replacement_position` with the given `sample_from_source` and `remaining` - if sample_from_source < rand_max: + if sample_from_source < sample_max: # Use random number to get `replacement_position`, where it's not `index` replacement_position = (sample_from_source % remaining) + index # Swap the index-th and replacement_position-th elements