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
91 changes: 35 additions & 56 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 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
| `MAX_CASPER_VOTES` | `2**10` (= 1,024) | votes |
| `LATEST_BLOCK_ROOTS_LENGTH` | `2**13` (= 8,192) | block roots |
| `LATEST_RANDAO_MIXES_LENGTH` | `2**13` (= 8,192) | randao mixes |
| `LATEST_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 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
| `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 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
'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 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
'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 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
# 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 @@ Unless otherwise indicated, code appearing in `this style` is to be interpreted
}
```

#### `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,19 @@ 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) -> Hash32:
vbuterin marked this conversation as resolved.
Show resolved Hide resolved
"""
Returns the randao mix at a recent ``slot``.
vbuterin marked this conversation as resolved.
Show resolved Hide resolved
"""
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]
vbuterin marked this conversation as resolved.
Show resolved Hide resolved
```

#### `get_beacon_proposer_index`

```python
Expand Down Expand Up @@ -1235,7 +1229,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 +1237,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 +1252,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 +1376,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 +1390,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 +1558,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 @@ -1762,7 +1738,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 @@ -1774,7 +1750,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 @@ -1790,17 +1766,19 @@ and perform the following updates:

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

* Set `state.previous_epoch_calculation_slot = state.current_epoch_calculation_slot`
* 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`.
* 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 @@ -1843,6 +1821,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]`
* 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))`
vbuterin marked this conversation as resolved.
Show resolved Hide resolved

## State root processing

Expand Down