Contents:

* Peg entries and exits to epoch boundaries
* Add a store of historical active index roots
* Mix it into the randomness
* Remove the delta hash chain

Note that the actual light client implementation is beyond the scope of the spec.

[Note to reviewers: verify that the invariant added in the PR is correct]

Question:

* Do we want to also only store epoch-boundary randao values? I don't think we use the epoch-intermediate ones anywhere.....
This commit is contained in:
vbuterin 2019-01-18 21:06:21 -06:00 committed by GitHub
parent 8d5c75c6fd
commit 9e75a76fc1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 35 additions and 56 deletions

View File

@ -18,7 +18,6 @@
- [Reward and penalty quotients](#reward-and-penalty-quotients) - [Reward and penalty quotients](#reward-and-penalty-quotients)
- [Status flags](#status-flags) - [Status flags](#status-flags)
- [Max operations per block](#max-operations-per-block) - [Max operations per block](#max-operations-per-block)
- [Validator registry delta flags](#validator-registry-delta-flags)
- [Signature domains](#signature-domains) - [Signature domains](#signature-domains)
- [Data structures](#data-structures) - [Data structures](#data-structures)
- [Beacon chain operations](#beacon-chain-operations) - [Beacon chain operations](#beacon-chain-operations)
@ -47,7 +46,6 @@
- [`Crosslink`](#crosslink) - [`Crosslink`](#crosslink)
- [`PendingAttestation`](#pendingattestation) - [`PendingAttestation`](#pendingattestation)
- [`Fork`](#fork) - [`Fork`](#fork)
- [`ValidatorRegistryDeltaBlock`](#validatorregistrydeltablock)
- [`Eth1Data`](#eth1data) - [`Eth1Data`](#eth1data)
- [`Eth1DataVote`](#eth1datavote) - [`Eth1DataVote`](#eth1datavote)
- [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract) - [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract)
@ -73,6 +71,7 @@
- [`get_crosslink_committees_at_slot`](#get_crosslink_committees_at_slot) - [`get_crosslink_committees_at_slot`](#get_crosslink_committees_at_slot)
- [`get_block_root`](#get_block_root) - [`get_block_root`](#get_block_root)
- [`get_randao_mix`](#get_randao_mix) - [`get_randao_mix`](#get_randao_mix)
- [`get_active_index_root`](#get_active_index_root)
- [`get_beacon_proposer_index`](#get_beacon_proposer_index) - [`get_beacon_proposer_index`](#get_beacon_proposer_index)
- [`merkle_root`](#merkle_root) - [`merkle_root`](#merkle_root)
- [`get_attestation_participants`](#get_attestation_participants) - [`get_attestation_participants`](#get_attestation_participants)
@ -168,6 +167,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
| `MAX_CASPER_VOTES` | `2**10` (= 1,024) | votes | | `MAX_CASPER_VOTES` | `2**10` (= 1,024) | votes |
| `LATEST_BLOCK_ROOTS_LENGTH` | `2**13` (= 8,192) | block roots | | `LATEST_BLOCK_ROOTS_LENGTH` | `2**13` (= 8,192) | block roots |
| `LATEST_RANDAO_MIXES_LENGTH` | `2**13` (= 8,192) | randao mixes | | `LATEST_RANDAO_MIXES_LENGTH` | `2**13` (= 8,192) | randao mixes |
| `LATEST_INDEX_ROOTS_LENGTH` | `2**13` (= 8,192) | index roots |
| `LATEST_PENALIZED_EXIT_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days | | `LATEST_PENALIZED_EXIT_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days |
| `MAX_WITHDRAWALS_PER_EPOCH` | `2**2` (= 4) | withdrawals | | `MAX_WITHDRAWALS_PER_EPOCH` | `2**2` (= 4) | withdrawals |
@ -235,13 +235,6 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
| `MAX_DEPOSITS` | `2**4` (= 16) | | `MAX_DEPOSITS` | `2**4` (= 16) |
| `MAX_EXITS` | `2**4` (= 16) | | `MAX_EXITS` | `2**4` (= 16) |
### Validator registry delta flags
| Name | Value |
| - | - |
| `ACTIVATION` | `0` |
| `EXIT` | `1` |
### Signature domains ### Signature domains
| Name | Value | | Name | Value |
@ -478,7 +471,6 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
'validator_balances': ['uint64'], 'validator_balances': ['uint64'],
'validator_registry_update_slot': 'uint64', 'validator_registry_update_slot': 'uint64',
'validator_registry_exit_count': 'uint64', 'validator_registry_exit_count': 'uint64',
'validator_registry_delta_chain_tip': 'hash32', # For light clients to track deltas
# Randomness and committees # Randomness and committees
'latest_randao_mixes': ['hash32'], 'latest_randao_mixes': ['hash32'],
@ -487,8 +479,8 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
'current_epoch_start_shard': 'uint64', 'current_epoch_start_shard': 'uint64',
'previous_epoch_calculation_slot': 'uint64', 'previous_epoch_calculation_slot': 'uint64',
'current_epoch_calculation_slot': 'uint64', 'current_epoch_calculation_slot': 'uint64',
'previous_epoch_randao_mix': 'hash32', 'previous_epoch_seed': 'hash32',
'current_epoch_randao_mix': 'hash32', 'current_epoch_seed': 'hash32',
# Custody challenges # Custody challenges
'custody_challenges': [CustodyChallenge], 'custody_challenges': [CustodyChallenge],
@ -502,6 +494,7 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
# Recent state # Recent state
'latest_crosslinks': [Crosslink], 'latest_crosslinks': [Crosslink],
'latest_block_roots': ['hash32'], # Needed to process attestations, older to newer 'latest_block_roots': ['hash32'], # Needed to process attestations, older to newer
'latest_index_roots': ['hash32'],
'latest_penalized_balances': ['uint64'], # Balances penalized at every withdrawal period 'latest_penalized_balances': ['uint64'], # Balances penalized at every withdrawal period
'latest_attestations': [PendingAttestation], 'latest_attestations': [PendingAttestation],
'batched_block_roots': ['hash32'], 'batched_block_roots': ['hash32'],
@ -584,18 +577,6 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
} }
``` ```
#### `ValidatorRegistryDeltaBlock`
```python
{
'latest_registry_delta_root': 'hash32',
'validator_index': 'uint24',
'pubkey': 'uint384',
'slot': 'uint64',
'flag': 'uint64',
}
```
#### `Eth1Data` #### `Eth1Data`
```python ```python
@ -954,7 +935,7 @@ def get_crosslink_committees_at_slot(state: BeaconState,
if slot < state_epoch_slot: if slot < state_epoch_slot:
committees_per_slot = get_previous_epoch_committee_count_per_slot(state) committees_per_slot = get_previous_epoch_committee_count_per_slot(state)
shuffling = get_shuffling( shuffling = get_shuffling(
state.previous_epoch_randao_mix, state.previous_epoch_seed,
state.validator_registry, state.validator_registry,
state.previous_epoch_calculation_slot, state.previous_epoch_calculation_slot,
) )
@ -962,7 +943,7 @@ def get_crosslink_committees_at_slot(state: BeaconState,
else: else:
committees_per_slot = get_current_epoch_committee_count_per_slot(state) committees_per_slot = get_current_epoch_committee_count_per_slot(state)
shuffling = get_shuffling( shuffling = get_shuffling(
state.current_epoch_randao_mix, state.current_epoch_seed,
state.validator_registry, state.validator_registry,
state.current_epoch_calculation_slot, state.current_epoch_calculation_slot,
) )
@ -1007,6 +988,19 @@ def get_randao_mix(state: BeaconState,
return state.latest_randao_mixes[slot % LATEST_RANDAO_MIXES_LENGTH] return state.latest_randao_mixes[slot % LATEST_RANDAO_MIXES_LENGTH]
``` ```
#### `get_active_index_root`
```python
def get_active_index_root(state: BeaconState,
slot: int) -> Hash32:
"""
Returns the randao mix at a recent ``slot``.
"""
assert state.slot // EPOCH_LENGTH < slot // EPOCH_LENGTH + LATEST_INDEX_ROOTS_LENGTH
assert slot // EPOCH_LENGTH <= state.slot // EPOCH_LENGTH
return state.latest_index_roots[(slot // EPOCH_LENGTH) % LATEST_INDEX_ROOTS_LENGTH]
```
#### `get_beacon_proposer_index` #### `get_beacon_proposer_index`
```python ```python
@ -1235,7 +1229,6 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit],
validator_balances=[], validator_balances=[],
validator_registry_update_slot=GENESIS_SLOT, validator_registry_update_slot=GENESIS_SLOT,
validator_registry_exit_count=0, validator_registry_exit_count=0,
validator_registry_delta_chain_tip=ZERO_HASH,
# Randomness and committees # Randomness and committees
latest_randao_mixes=[ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH)], latest_randao_mixes=[ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH)],
@ -1244,8 +1237,8 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit],
current_epoch_start_shard=GENESIS_START_SHARD, current_epoch_start_shard=GENESIS_START_SHARD,
previous_epoch_calculation_slot=GENESIS_SLOT, previous_epoch_calculation_slot=GENESIS_SLOT,
current_epoch_calculation_slot=GENESIS_SLOT, current_epoch_calculation_slot=GENESIS_SLOT,
previous_epoch_randao_mix=ZERO_HASH, previous_epoch_seed=ZERO_HASH,
current_epoch_randao_mix=ZERO_HASH, current_epoch_seed=ZERO_HASH,
# Custody challenges # Custody challenges
custody_challenges=[], custody_challenges=[],
@ -1259,6 +1252,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit],
# Recent state # Recent state
latest_crosslinks=[Crosslink(slot=GENESIS_SLOT, shard_block_root=ZERO_HASH) for _ in range(SHARD_COUNT)], latest_crosslinks=[Crosslink(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_block_roots=[ZERO_HASH for _ in range(LATEST_BLOCK_ROOTS_LENGTH)],
latest_index_roots=[ZERO_HASH for _ in range(LATEST_INDEX_ROOTS_LENGTH)],
latest_penalized_balances=[0 for _ in range(LATEST_PENALIZED_EXIT_LENGTH)], latest_penalized_balances=[0 for _ in range(LATEST_PENALIZED_EXIT_LENGTH)],
latest_attestations=[], latest_attestations=[],
batched_block_roots=[], batched_block_roots=[],
@ -1383,16 +1377,7 @@ Note: All functions in this section mutate `state`.
def activate_validator(state: BeaconState, index: int, genesis: bool) -> None: def activate_validator(state: BeaconState, index: int, genesis: bool) -> None:
validator = state.validator_registry[index] validator = state.validator_registry[index]
validator.activation_slot = GENESIS_SLOT if genesis else (state.slot + ENTRY_EXIT_DELAY) validator.activation_slot = GENESIS_SLOT if genesis else (state.slot - state.slot % EPOCH_LENGTH + ENTRY_EXIT_DELAY)
state.validator_registry_delta_chain_tip = hash_tree_root(
ValidatorRegistryDeltaBlock(
latest_registry_delta_root=state.validator_registry_delta_chain_tip,
validator_index=index,
pubkey=validator.pubkey,
slot=validator.activation_slot,
flag=ACTIVATION,
)
)
``` ```
```python ```python
@ -1406,22 +1391,13 @@ def exit_validator(state: BeaconState, index: int) -> None:
validator = state.validator_registry[index] validator = state.validator_registry[index]
# The following updates only occur if not previous exited # The following updates only occur if not previous exited
if validator.exit_slot <= state.slot + ENTRY_EXIT_DELAY: if validator.exit_slot <= state.slot - state.slot % EPOCH_LENGTH + ENTRY_EXIT_DELAY:
return return
validator.exit_slot = state.slot + ENTRY_EXIT_DELAY validator.exit_slot = state.slot - state.slot % EPOCH_LENGTH + ENTRY_EXIT_DELAY
state.validator_registry_exit_count += 1 state.validator_registry_exit_count += 1
validator.exit_count = state.validator_registry_exit_count validator.exit_count = state.validator_registry_exit_count
state.validator_registry_delta_chain_tip = hash_tree_root(
ValidatorRegistryDeltaBlock(
latest_registry_delta_root=state.validator_registry_delta_chain_tip,
validator_index=index,
pubkey=validator.pubkey,
slot=validator.exit_slot,
flag=EXIT,
)
)
``` ```
```python ```python
@ -1583,7 +1559,7 @@ Verify that `len(block.body.exits) <= MAX_EXITS`.
For each `exit` in `block.body.exits`: For each `exit` in `block.body.exits`:
* Let `validator = state.validator_registry[exit.validator_index]`. * Let `validator = state.validator_registry[exit.validator_index]`.
* Verify that `validator.exit_slot > state.slot + ENTRY_EXIT_DELAY`. * Verify that `validator.exit_slot > state.slot - state.slot % EPOCH_LENGTH + ENTRY_EXIT_DELAY`.
* Verify that `state.slot >= exit.slot`. * Verify that `state.slot >= exit.slot`.
* Let `exit_message = hash_tree_root(Exit(slot=exit.slot, validator_index=exit.validator_index, signature=EMPTY_SIGNATURE))`. * Let `exit_message = hash_tree_root(Exit(slot=exit.slot, validator_index=exit.validator_index, signature=EMPTY_SIGNATURE))`.
* Verify that `bls_verify(pubkey=validator.pubkey, message=exit_message, signature=exit.signature, domain=get_domain(state.fork, exit.slot, DOMAIN_EXIT))`. * Verify that `bls_verify(pubkey=validator.pubkey, message=exit_message, signature=exit.signature, domain=get_domain(state.fork, exit.slot, DOMAIN_EXIT))`.
@ -1763,7 +1739,7 @@ def update_validator_registry(state: BeaconState) -> None:
# Activate validators within the allowable balance churn # Activate validators within the allowable balance churn
balance_churn = 0 balance_churn = 0
for index, validator in enumerate(state.validator_registry): for index, validator in enumerate(state.validator_registry):
if validator.activation_slot > state.slot + ENTRY_EXIT_DELAY and state.validator_balances[index] >= MAX_DEPOSIT_AMOUNT: if validator.activation_slot > state.slot - state.slot % EPOCH_LENGTH + ENTRY_EXIT_DELAY and state.validator_balances[index] >= MAX_DEPOSIT_AMOUNT:
# Check the balance churn would be within the allowance # Check the balance churn would be within the allowance
balance_churn += get_effective_balance(state, index) balance_churn += get_effective_balance(state, index)
if balance_churn > max_balance_churn: if balance_churn > max_balance_churn:
@ -1775,7 +1751,7 @@ def update_validator_registry(state: BeaconState) -> None:
# Exit validators within the allowable balance churn # Exit validators within the allowable balance churn
balance_churn = 0 balance_churn = 0
for index, validator in enumerate(state.validator_registry): for index, validator in enumerate(state.validator_registry):
if validator.exit_slot > state.slot + ENTRY_EXIT_DELAY and validator.status_flags & INITIATED_EXIT: if validator.exit_slot > state.slot - state.slot % EPOCH_LENGTH + ENTRY_EXIT_DELAY and validator.status_flags & INITIATED_EXIT:
# Check the balance churn would be within the allowance # Check the balance churn would be within the allowance
balance_churn += get_effective_balance(state, index) balance_churn += get_effective_balance(state, index)
if balance_churn > max_balance_churn: if balance_churn > max_balance_churn:
@ -1791,17 +1767,19 @@ and perform the following updates:
* Set `state.previous_epoch_calculation_slot = state.current_epoch_calculation_slot` * Set `state.previous_epoch_calculation_slot = state.current_epoch_calculation_slot`
* Set `state.previous_epoch_start_shard = state.current_epoch_start_shard` * Set `state.previous_epoch_start_shard = state.current_epoch_start_shard`
* Set `state.previous_epoch_randao_mix = state.current_epoch_randao_mix` * Set `state.previous_epoch_seed = state.current_epoch_seed`
* Set `state.current_epoch_calculation_slot = state.slot` * Set `state.current_epoch_calculation_slot = state.slot`
* Set `state.current_epoch_start_shard = (state.current_epoch_start_shard + get_current_epoch_committee_count_per_slot(state) * EPOCH_LENGTH) % SHARD_COUNT` * Set `state.current_epoch_start_shard = (state.current_epoch_start_shard + get_current_epoch_committee_count_per_slot(state) * EPOCH_LENGTH) % SHARD_COUNT`
* Set `state.current_epoch_randao_mix = get_randao_mix(state, state.current_epoch_calculation_slot - SEED_LOOKAHEAD)` * Set `state.current_epoch_seed = hash(get_randao_mix(state, state.current_epoch_calculation_slot - SEED_LOOKAHEAD) + get_active_index_root(state, state.current_epoch_calculation_slot))`
If a validator registry update does _not_ happen do the following: If a validator registry update does _not_ happen do the following:
* Set `state.previous_epoch_calculation_slot = state.current_epoch_calculation_slot` * Set `state.previous_epoch_calculation_slot = state.current_epoch_calculation_slot`
* Set `state.previous_epoch_start_shard = state.current_epoch_start_shard` * Set `state.previous_epoch_start_shard = state.current_epoch_start_shard`
* Let `epochs_since_last_registry_change = (state.slot - state.validator_registry_update_slot) // EPOCH_LENGTH`. * Let `epochs_since_last_registry_change = (state.slot - state.validator_registry_update_slot) // EPOCH_LENGTH`.
* If `epochs_since_last_registry_change` is an exact power of 2, set `state.current_epoch_calculation_slot = state.slot` and `state.current_epoch_randao_mix = state.latest_randao_mixes[(state.current_epoch_calculation_slot - SEED_LOOKAHEAD) % LATEST_RANDAO_MIXES_LENGTH]`. Note that `state.current_epoch_start_shard` is left unchanged. * If `epochs_since_last_registry_change` is an exact power of 2, set `state.current_epoch_calculation_slot = state.slot` and `state.current_epoch_seed = hash(get_randao_mix(state, state.current_epoch_calculation_slot - SEED_LOOKAHEAD) + get_active_index_root(state, state.current_epoch_calculation_slot))`. Note that `state.current_epoch_start_shard` is left unchanged.
**Invariant**: the active index root that is hashed into the shuffling seed actually is the `hash_tree_root` of the validator set that is used for that epoch.
Regardless of whether or not a validator set change happens, run the following: Regardless of whether or not a validator set change happens, run the following:
@ -1844,6 +1822,7 @@ def process_penalties_and_exits(state: BeaconState) -> None:
* Let `e = state.slot // EPOCH_LENGTH`. Set `state.latest_penalized_balances[(e+1) % LATEST_PENALIZED_EXIT_LENGTH] = state.latest_penalized_balances[e % LATEST_PENALIZED_EXIT_LENGTH]` * Let `e = state.slot // EPOCH_LENGTH`. Set `state.latest_penalized_balances[(e+1) % LATEST_PENALIZED_EXIT_LENGTH] = state.latest_penalized_balances[e % LATEST_PENALIZED_EXIT_LENGTH]`
* Remove any `attestation` in `state.latest_attestations` such that `attestation.data.slot < state.slot - EPOCH_LENGTH`. * Remove any `attestation` in `state.latest_attestations` such that `attestation.data.slot < state.slot - EPOCH_LENGTH`.
* Let `epoch = state.slot // EPOCH_LENGTH`. Set `state.latest_index_roots[epoch % LATEST_INDEX_ROOTS_LENGTH] = hash_tree_root(get_active_validator_indices(state, state.slot))`
## State root processing ## State root processing