Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement #459 (light client friendliness) #476

Merged
merged 14 commits into from
Jan 25, 2019
96 changes: 39 additions & 57 deletions specs/core/0_beacon-chain.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
- [Reward and penalty quotients](#reward-and-penalty-quotients)
- [Status flags](#status-flags)
- [Max operations per block](#max-operations-per-block)
- [Validator registry delta flags](#validator-registry-delta-flags)
- [Signature domains](#signature-domains)
- [Data structures](#data-structures)
- [Beacon chain operations](#beacon-chain-operations)
Expand Down Expand Up @@ -47,7 +46,6 @@
- [`Crosslink`](#crosslink)
- [`PendingAttestation`](#pendingattestation)
- [`Fork`](#fork)
- [`ValidatorRegistryDeltaBlock`](#validatorregistrydeltablock)
- [`Eth1Data`](#eth1data)
- [`Eth1DataVote`](#eth1datavote)
- [Ethereum 1.0 deposit contract](#ethereum-10-deposit-contract)
Expand All @@ -73,6 +71,7 @@
- [`get_crosslink_committees_at_slot`](#get_crosslink_committees_at_slot)
- [`get_block_root`](#get_block_root)
- [`get_randao_mix`](#get_randao_mix)
- [`get_active_index_root`](#get_active_index_root)
- [`get_beacon_proposer_index`](#get_beacon_proposer_index)
- [`merkle_root`](#merkle_root)
- [`get_attestation_participants`](#get_attestation_participants)
Expand Down Expand Up @@ -168,6 +167,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be
| `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_INDEX_ROOTS_LENGTH` | `2**13` (= 8,192) | index roots |
| `LATEST_PENALIZED_EXIT_LENGTH` | `2**13` (= 8,192) | epochs | ~36 days |
| `MAX_WITHDRAWALS_PER_EPOCH` | `2**2` (= 4) | withdrawals |

Expand Down Expand Up @@ -235,13 +235,6 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be
| `MAX_DEPOSITS` | `2**4` (= 16) |
| `MAX_EXITS` | `2**4` (= 16) |

### Validator registry delta flags

| Name | Value |
| - | - |
| `ACTIVATION` | `0` |
| `EXIT` | `1` |

### Signature domains

| Name | Value |
Expand Down Expand Up @@ -478,7 +471,6 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be
'validator_balances': ['uint64'],
'validator_registry_update_slot': 'uint64',
'validator_registry_exit_count': 'uint64',
'validator_registry_delta_chain_tip': 'bytes32', # For light clients to track deltas

# Randomness and committees
'latest_randao_mixes': ['bytes32'],
Expand All @@ -487,8 +479,8 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be
'current_epoch_start_shard': 'uint64',
'previous_epoch_calculation_slot': 'uint64',
'current_epoch_calculation_slot': 'uint64',
'previous_epoch_randao_mix': 'bytes32',
'current_epoch_randao_mix': 'bytes32',
'previous_epoch_seed': 'bytes32',
'current_epoch_seed': 'bytes32',

# Custody challenges
'custody_challenges': [CustodyChallenge],
Expand All @@ -502,6 +494,7 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be
# Recent state
'latest_crosslinks': [Crosslink],
'latest_block_roots': ['bytes32'], # Needed to process attestations, older to newer
'latest_index_roots': ['bytes32'],
'latest_penalized_balances': ['uint64'], # Balances penalized at every withdrawal period
'latest_attestations': [PendingAttestation],
'batched_block_roots': ['bytes32'],
Expand Down Expand Up @@ -584,18 +577,6 @@ Code snippets appearing in `this style` are to be interpreted as Python code. Be
}
```

#### `ValidatorRegistryDeltaBlock`

```python
{
'latest_registry_delta_root': 'bytes32',
'validator_index': 'uint24',
'pubkey': 'bytes48',
'slot': 'uint64',
'flag': 'uint64',
}
```

#### `Eth1Data`

```python
Expand Down Expand Up @@ -954,15 +935,15 @@ def get_crosslink_committees_at_slot(state: BeaconState,
if slot < state_epoch_slot:
committees_per_slot = get_previous_epoch_committee_count_per_slot(state)
shuffling = get_shuffling(
state.previous_epoch_randao_mix,
state.previous_epoch_seed,
state.validator_registry,
state.previous_epoch_calculation_slot,
)
slot_start_shard = (state.previous_epoch_start_shard + committees_per_slot * offset) % SHARD_COUNT
else:
committees_per_slot = get_current_epoch_committee_count_per_slot(state)
shuffling = get_shuffling(
state.current_epoch_randao_mix,
state.current_epoch_seed,
state.validator_registry,
state.current_epoch_calculation_slot,
)
Expand Down Expand Up @@ -1007,6 +988,21 @@ def get_randao_mix(state: BeaconState,
return state.latest_randao_mixes[slot % LATEST_RANDAO_MIXES_LENGTH]
```

#### `get_active_index_root`

```python
def get_active_index_root(state: BeaconState,
slot: int) -> Bytes32:
"""
Returns the index root at a recent ``slot``.
"""
state_epoch = state.slot // EPOCH_LENGTH
given_epoch = slot // EPOCH_LENGTH
assert state_epoch < given_epoch + LATEST_INDEX_ROOTS_LENGTH
assert given_epoch <= state_epoch
return state.latest_index_roots[given_epoch % LATEST_INDEX_ROOTS_LENGTH]
```

#### `get_beacon_proposer_index`

```python
Expand Down Expand Up @@ -1235,7 +1231,6 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit],
validator_balances=[],
validator_registry_update_slot=GENESIS_SLOT,
validator_registry_exit_count=0,
validator_registry_delta_chain_tip=ZERO_HASH,

# Randomness and committees
latest_randao_mixes=[ZERO_HASH for _ in range(LATEST_RANDAO_MIXES_LENGTH)],
Expand All @@ -1244,8 +1239,8 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit],
current_epoch_start_shard=GENESIS_START_SHARD,
previous_epoch_calculation_slot=GENESIS_SLOT,
current_epoch_calculation_slot=GENESIS_SLOT,
previous_epoch_randao_mix=ZERO_HASH,
current_epoch_randao_mix=ZERO_HASH,
previous_epoch_seed=ZERO_HASH,
current_epoch_seed=ZERO_HASH,

# Custody challenges
custody_challenges=[],
Expand All @@ -1259,6 +1254,7 @@ def get_initial_beacon_state(initial_validator_deposits: List[Deposit],
# Recent state
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_index_roots=[ZERO_HASH for _ in range(LATEST_INDEX_ROOTS_LENGTH)],
latest_penalized_balances=[0 for _ in range(LATEST_PENALIZED_EXIT_LENGTH)],
latest_attestations=[],
batched_block_roots=[],
Expand Down Expand Up @@ -1382,16 +1378,7 @@ Note: All functions in this section mutate `state`.
def activate_validator(state: BeaconState, index: int, genesis: bool) -> None:
validator = state.validator_registry[index]

validator.activation_slot = GENESIS_SLOT if genesis else (state.slot + 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,
)
)
validator.activation_slot = GENESIS_SLOT if genesis else (state.slot - state.slot % EPOCH_LENGTH + ENTRY_EXIT_DELAY)
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
```

```python
Expand All @@ -1405,22 +1392,13 @@ def exit_validator(state: BeaconState, index: int) -> None:
validator = state.validator_registry[index]

# 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:
djrtwo marked this conversation as resolved.
Show resolved Hide resolved
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
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
Expand Down Expand Up @@ -1582,7 +1560,7 @@ 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.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`.
* 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))`.
Expand Down Expand Up @@ -1767,7 +1745,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 + 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
balance_churn += get_effective_balance(state, index)
if balance_churn > max_balance_churn:
Expand All @@ -1779,7 +1757,7 @@ def update_validator_registry(state: BeaconState) -> None:
# Exit validators within the allowable balance churn
balance_churn = 0
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
balance_churn += get_effective_balance(state, index)
if balance_churn > max_balance_churn:
Expand All @@ -1793,15 +1771,17 @@ def update_validator_registry(state: BeaconState) -> None:

and perform the following updates:

* 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_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))`
vbuterin marked this conversation as resolved.
Show resolved Hide resolved

If a validator registry update does _not_ happen do the following:

* 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.
vbuterin marked this conversation as resolved.
Show resolved Hide resolved

**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:

Expand Down Expand Up @@ -1842,8 +1822,10 @@ def process_penalties_and_exits(state: BeaconState) -> None:

### Final updates

* 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 `epoch = state.slot // EPOCH_LENGTH`.
* Set `state.latest_penalized_balances[(epoch+1) % LATEST_PENALIZED_EXIT_LENGTH] = state.latest_penalized_balances[epoch % LATEST_PENALIZED_EXIT_LENGTH]`
* Remove any `attestation` in `state.latest_attestations` such that `attestation.data.slot < 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

Expand Down